Compare commits

..

48 Commits

Author SHA1 Message Date
Hargata Softworks
3c2715840e Merge pull request #233 from hargata/Hargata/increase.upload.size
1.1.3
2024-02-05 18:29:36 -07:00
DESKTOP-GENO133\IvanPlex
dfbd90aaf4 Merge branch 'main' into Hargata/increase.upload.size
# Conflicts:
#	Views/Login/Index.cshtml
2024-02-05 18:26:14 -07:00
DESKTOP-GENO133\IvanPlex
2b3a4ce8ce more things to translate 2024-02-05 18:23:06 -07:00
DESKTOP-GENO133\IvanPlex
135292f2b4 added attachment features to Notes. 2024-02-05 18:08:54 -07:00
Hargata Softworks
499c697b0b Merge pull request #229 from hargata/Hargata/redirectURL
added redirectURL
2024-02-05 08:46:05 -07:00
DESKTOP-T0O5CDB\DESK-555BD
fb74f01609 added redirectURL 2024-02-05 08:44:49 -07:00
DESKTOP-GENO133\IvanPlex
6ab2216742 greatly increase the max upload size. 2024-02-04 15:32:34 -07:00
Hargata Softworks
7c97aa70a0 Merge pull request #228 from hargata/Hargata/translation.helper
added try catch to translation layer for translation files error.
2024-02-04 12:56:51 -07:00
DESKTOP-GENO133\IvanPlex
ff255e8293 added try catch to translation layer for translation files error. 2024-02-04 12:56:24 -07:00
Hargata Softworks
57dc976a9f Merge pull request #227 from jdh313/docker-image-tags
ci: generate versioned tags
2024-02-04 11:46:27 -07:00
Jacob Hoehler
13361241e8 ci: generate versioned tags
Generates an image tag based on release tag, as well as generating a `latest` tag. Also generates an `edge` tag for the latest pushes to the main branch.
2024-02-04 13:17:45 -05:00
Hargata Softworks
332af9a04a Merge pull request #224 from hargata/Hargata/translation.helper
UI Translation
2024-02-04 10:55:09 -07:00
DESKTOP-GENO133\IvanPlex
961c60f936 fixed menu styling. 2024-02-04 10:53:23 -07:00
DESKTOP-GENO133\IvanPlex
34483886be added translation volume so that it can persist. 2024-02-04 10:49:22 -07:00
DESKTOP-GENO133\IvanPlex
548989f9ae keyed admin panel 2024-02-04 10:09:57 -07:00
DESKTOP-GENO133\IvanPlex
83a59aff37 set different path for default language. 2024-02-03 22:49:18 -07:00
DESKTOP-GENO133\IvanPlex
60f28b16e7 Merge remote-tracking branch 'origin/main' into Hargata/translation.helper 2024-02-03 22:30:52 -07:00
DESKTOP-GENO133\IvanPlex
9cb8bbd5b7 added default language settings. 2024-02-03 22:15:53 -07:00
DESKTOP-GENO133\IvanPlex
f7c95e9b36 more forgotten ones. 2024-02-03 22:11:47 -07:00
DESKTOP-GENO133\IvanPlex
cd37eedd15 updated default translation file. 2024-02-03 22:10:42 -07:00
DESKTOP-GENO133\IvanPlex
7d11c4003f keyed the rest. 2024-02-03 21:58:17 -07:00
DESKTOP-GENO133\IvanPlex
7d9b67a04d keyed taxes 2024-02-03 20:51:18 -07:00
DESKTOP-GENO133\IvanPlex
aba178a324 fix html encodings. 2024-02-03 20:36:32 -07:00
Hargata Softworks
4ecdaf5225 Merge pull request #225 from hargata/Hargata/unified.error.message
Fixed Odometer Validation
2024-02-03 20:16:39 -07:00
DESKTOP-GENO133\IvanPlex
c0bc9f7a14 fix odometer validation. 2024-02-03 20:13:59 -07:00
DESKTOP-GENO133\IvanPlex
094d96d880 unified error messages. 2024-02-03 20:07:05 -07:00
DESKTOP-GENO133\IvanPlex
a8fad6f160 keyed supplies 2024-02-03 18:46:21 -07:00
DESKTOP-GENO133\IvanPlex
6befd5ca76 keyed service record. 2024-02-03 17:51:26 -07:00
DESKTOP-GENO133\IvanPlex
046812ff27 keyed reminders and report page. 2024-02-03 16:51:07 -07:00
DESKTOP-GENO133\IvanPlex
db1778a749 keyed plans 2024-02-03 15:38:57 -07:00
DESKTOP-GENO133\IvanPlex
f27fa1625c missed a few tags. 2024-02-03 15:13:59 -07:00
DESKTOP-GENO133\IvanPlex
39a9d5f95b keyed up to odometer. 2024-02-03 14:41:33 -07:00
DESKTOP-GENO133\IvanPlex
234d7163fc added caching to reduce number of IO reads. 2024-02-03 11:01:54 -07:00
DESKTOP-GENO133\IvanPlex
6f64cc9996 keyed labels. 2024-02-03 10:10:15 -07:00
DESKTOP-GENO133\IvanPlex
58e3b49bdf updated en_US translation. 2024-02-03 10:04:47 -07:00
DESKTOP-GENO133\IvanPlex
ca2dc1e1e0 keyed gas modal. 2024-02-03 10:03:38 -07:00
DESKTOP-GENO133\IvanPlex
c65dca4c58 removed hardcoded string from gasrecord js. 2024-02-03 09:57:28 -07:00
DESKTOP-GENO133\IvanPlex
82c2351b00 keyed gas tab. 2024-02-03 09:51:42 -07:00
DESKTOP-GENO133\IvanPlex
f31ca163e5 Merge branch 'Hargata/tags.export' into Hargata/translation.helper 2024-02-03 06:28:32 -07:00
Hargata Softworks
351db5de95 Merge pull request #222 from hargata/Hargata/tags.export
added tags export to csv.
2024-02-03 06:13:06 -07:00
DESKTOP-GENO133\IvanPlex
41164516f7 added tags export to csv. 2024-02-03 06:11:59 -07:00
DESKTOP-GENO133\IvanPlex
29a3c815fc added translation helper. 2024-02-02 21:29:44 -07:00
Hargata Softworks
1768063e57 Merge pull request #218 from hargata/Hargata/update.count
Update count and aggregates after filtering by tags.
2024-02-02 17:05:02 -07:00
Hargata Softworks
35ce5603e5 Merge pull request #219 from hargata/Hargata/gas.tags
Hargata/gas.tags
2024-02-02 17:04:28 -07:00
DESKTOP-GENO133\IvanPlex
19e38f9885 fixed gas odometer reading validation. 2024-02-02 17:03:56 -07:00
DESKTOP-GENO133\IvanPlex
c55528b8a0 Re-calculate gas tag aggregate labels. 2024-02-02 16:58:37 -07:00
DESKTOP-GENO133\IvanPlex
1c00f31312 added tags functionality to gas tab. 2024-02-02 15:33:02 -07:00
DESKTOP-T0O5CDB\DESK-555BD
8e66530544 Update count and aggregates after filtering by tags. 2024-02-02 12:15:34 -07:00
79 changed files with 1353 additions and 761 deletions

View File

@@ -2,7 +2,9 @@ name: Build and Push Image to Dockerhub and GHCR
on:
push:
branches: [ "main" ]
branches: ["main"]
release:
types: ["published"]
jobs:
build:
@@ -10,26 +12,40 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: "${{ secrets.DH_USER }}"
password: "${{ secrets.DH_PASS }}"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: "hargata"
password: "${{ secrets.GHCR_PAT }}"
- name: Docker Metadata
id: meta
uses: docker/metadata-action@v5
with:
context: workflow
images: |
hargata/lubelogger
ghcr.io/hargata/lubelogger
tags: |
type=edge,branch=main
type=ref,event=tag
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
hargata/lubelogger:latest
ghcr.io/hargata/lubelogger:latest
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

1
.gitignore vendored
View File

@@ -7,3 +7,4 @@ data/cartracker.db
wwwroot/documents/
wwwroot/temp/
wwwroot/imports/
wwwroot/translations/

View File

@@ -33,6 +33,26 @@ namespace CarCareTracker.Controllers
return Json(fileName);
}
[HttpPost]
public IActionResult HandleTranslationFileUpload(IFormFile file)
{
var originalFileName = Path.GetFileNameWithoutExtension(file.FileName);
if (originalFileName == "en_US")
{
return Json(new OperationResponse { Success = false, Message = "The translation file name en_US is reserved." });
}
var fileName = UploadFile(file);
//move file from temp to translation folder.
var uploadedFilePath = _fileHelper.MoveFileFromTemp(fileName, "translations/");
//rename uploaded file so that it preserves original name.
if (!string.IsNullOrWhiteSpace(uploadedFilePath))
{
var result = _fileHelper.RenameFile(uploadedFilePath, originalFileName);
return Json(new OperationResponse { Success = result, Message = string.Empty });
}
return Json(new OperationResponse { Success = false, Message = StaticHelper.GenericErrorMessage });
}
[HttpPost]
public IActionResult HandleMultipleFileUpload(List<IFormFile> file)
{
@@ -44,7 +64,7 @@ namespace CarCareTracker.Controllers
}
return Json(uploadedFiles);
}
[Authorize(Roles = nameof(UserData.IsRootUser))]
[HttpPost]
public IActionResult DeleteFiles(string fileLocation)
{

View File

@@ -15,17 +15,20 @@ namespace CarCareTracker.Controllers
private readonly ILogger<HomeController> _logger;
private readonly IVehicleDataAccess _dataAccess;
private readonly IUserLogic _userLogic;
private readonly IFileHelper _fileHelper;
private readonly IConfigHelper _config;
public HomeController(ILogger<HomeController> logger,
IVehicleDataAccess dataAccess,
IUserLogic userLogic,
IConfigHelper configuration)
IConfigHelper configuration,
IFileHelper fileHelper)
{
_logger = logger;
_dataAccess = dataAccess;
_config = configuration;
_userLogic = userLogic;
_fileHelper = fileHelper;
}
private int GetUserID()
{
@@ -47,7 +50,13 @@ namespace CarCareTracker.Controllers
public IActionResult Settings()
{
var userConfig = _config.GetUserConfig(User);
return PartialView("_Settings", userConfig);
var languages = _fileHelper.GetLanguages();
var viewModel = new SettingsViewModel
{
UserConfig = userConfig,
UILanguages = languages
};
return PartialView("_Settings", viewModel);
}
[HttpPost]
public IActionResult WriteToSettings(UserConfig userConfig)

View File

@@ -26,9 +26,9 @@ namespace CarCareTracker.Controllers
_logger = logger;
_loginLogic = loginLogic;
}
public IActionResult Index()
public IActionResult Index(string redirectURL = "")
{
return View();
return View(model: redirectURL);
}
public IActionResult Registration()
{

View File

@@ -172,7 +172,7 @@ namespace CarCareTracker.Controllers
var vehicleRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
if (vehicleRecords.Any())
{
var exportData = vehicleRecords.Select(x => new ServiceRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Odometer = x.Mileage.ToString() });
var exportData = vehicleRecords.Select(x => new ServiceRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Odometer = x.Mileage.ToString(), Tags = string.Join(" ", x.Tags) });
using (var writer = new StreamWriter(fullExportFilePath))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
@@ -190,7 +190,7 @@ namespace CarCareTracker.Controllers
var vehicleRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId);
if (vehicleRecords.Any())
{
var exportData = vehicleRecords.Select(x => new ServiceRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Odometer = x.Mileage.ToString() });
var exportData = vehicleRecords.Select(x => new ServiceRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Odometer = x.Mileage.ToString(), Tags = string.Join(" ", x.Tags) });
using (var writer = new StreamWriter(fullExportFilePath))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
@@ -208,7 +208,7 @@ namespace CarCareTracker.Controllers
var vehicleRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId);
if (vehicleRecords.Any())
{
var exportData = vehicleRecords.Select(x => new ServiceRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Odometer = x.Mileage.ToString() });
var exportData = vehicleRecords.Select(x => new ServiceRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Odometer = x.Mileage.ToString(), Tags = string.Join(" ", x.Tags) });
using (var writer = new StreamWriter(fullExportFilePath))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
@@ -226,7 +226,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() });
var exportData = vehicleRecords.Select(x => new OdometerRecordExportModel { Date = x.Date.ToShortDateString(), Notes = x.Notes, Odometer = x.Mileage.ToString(), Tags = string.Join(" ", x.Tags) });
using (var writer = new StreamWriter(fullExportFilePath))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
@@ -271,7 +271,7 @@ namespace CarCareTracker.Controllers
var vehicleRecords = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId);
if (vehicleRecords.Any())
{
var exportData = vehicleRecords.Select(x => new TaxRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes });
var exportData = vehicleRecords.Select(x => new TaxRecordExportModel { Date = x.Date.ToShortDateString(), Description = x.Description, Cost = x.Cost.ToString("C"), Notes = x.Notes, Tags = string.Join(" ", x.Tags) });
using (var writer = new StreamWriter(fullExportFilePath))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
@@ -327,7 +327,8 @@ namespace CarCareTracker.Controllers
Odometer = x.Mileage.ToString(),
IsFillToFull = x.IsFillToFull.ToString(),
MissedFuelUp = x.MissedFuelUp.ToString(),
Notes = x.Notes
Notes = x.Notes,
Tags = string.Join(" ", x.Tags)
});
using (var writer = new StreamWriter(fullExportFilePath))
{
@@ -378,7 +379,8 @@ namespace CarCareTracker.Controllers
Date = DateTime.Parse(importModel.Date),
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
Gallons = decimal.Parse(importModel.FuelConsumed, NumberStyles.Any),
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
};
if (string.IsNullOrWhiteSpace(importModel.Cost) && !string.IsNullOrWhiteSpace(importModel.Price))
{
@@ -423,7 +425,8 @@ namespace CarCareTracker.Controllers
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Service Record on {importModel.Date}" : importModel.Description,
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any),
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
};
_serviceRecordDataAccess.SaveServiceRecordToVehicle(convertedRecord);
}
@@ -434,7 +437,8 @@ namespace CarCareTracker.Controllers
VehicleId = vehicleId,
Date = DateTime.Parse(importModel.Date),
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(convertedRecord);
}
@@ -466,7 +470,8 @@ namespace CarCareTracker.Controllers
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Repair Record on {importModel.Date}" : importModel.Description,
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any),
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
};
_collisionRecordDataAccess.SaveCollisionRecordToVehicle(convertedRecord);
}
@@ -479,7 +484,8 @@ namespace CarCareTracker.Controllers
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Upgrade Record on {importModel.Date}" : importModel.Description,
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any),
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
};
_upgradeRecordDataAccess.SaveUpgradeRecordToVehicle(convertedRecord);
}
@@ -506,7 +512,8 @@ namespace CarCareTracker.Controllers
Date = DateTime.Parse(importModel.Date),
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Tax Record on {importModel.Date}" : importModel.Description,
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any),
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
};
_taxRecordDataAccess.SaveTaxRecordToVehicle(convertedRecord);
}
@@ -589,7 +596,8 @@ namespace CarCareTracker.Controllers
Gallons = result.Gallons,
IsFillToFull = result.IsFillToFull,
MissedFuelUp = result.MissedFuelUp,
Notes = result.Notes
Notes = result.Notes,
Tags = result.Tags
};
var vehicleData = _dataAccess.GetVehicleById(convertedResult.VehicleId);
var vehicleIsElectric = vehicleData.IsElectric;

View File

@@ -10,6 +10,7 @@ namespace CarCareTracker.Helper
UserConfig GetUserConfig(ClaimsPrincipal user);
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
string GetLogoUrl();
string GetServerLanguage();
public bool DeleteUserConfig(int userId);
}
public class ConfigHelper : IConfigHelper
@@ -34,6 +35,11 @@ namespace CarCareTracker.Helper
}
return logoUrl;
}
public string GetServerLanguage()
{
var serverLanguage = _config[nameof(UserConfig.UserLanguage)] ?? "en_US";
return serverLanguage;
}
public bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData)
{
var storedUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
@@ -110,6 +116,7 @@ namespace CarCareTracker.Helper
EnableAutoOdometerInsert = bool.Parse(_config[nameof(UserConfig.EnableAutoOdometerInsert)]),
PreferredGasMileageUnit = _config[nameof(UserConfig.PreferredGasMileageUnit)],
PreferredGasUnit = _config[nameof(UserConfig.PreferredGasUnit)],
UserLanguage = _config[nameof(UserConfig.UserLanguage)],
VisibleTabs = _config.GetSection("VisibleTabs").Get<List<ImportMode>>(),
DefaultTab = (ImportMode)int.Parse(_config[nameof(UserConfig.DefaultTab)])
};

View File

@@ -7,10 +7,12 @@ namespace CarCareTracker.Helper
{
string GetFullFilePath(string currentFilePath, bool mustExist = true);
string MoveFileFromTemp(string currentFilePath, string newFolder);
bool RenameFile(string currentFilePath, string newName);
bool DeleteFile(string currentFilePath);
string MakeBackup();
bool RestoreBackup(string fileName, bool clearExisting = false);
string MakeAttachmentsExport(List<GenericReportModel> exportData);
List<string> GetLanguages();
}
public class FileHelper : IFileHelper
{
@@ -21,6 +23,40 @@ namespace CarCareTracker.Helper
_webEnv = webEnv;
_logger = logger;
}
public List<string> GetLanguages()
{
var languagePath = Path.Combine(_webEnv.WebRootPath, "translations");
var defaultList = new List<string>() { "en_US" };
if (Directory.Exists(languagePath))
{
var listOfLanguages = Directory.GetFiles(languagePath);
if (listOfLanguages.Any())
{
defaultList.AddRange(listOfLanguages.Select(x => Path.GetFileNameWithoutExtension(x)));
}
}
return defaultList;
}
public bool RenameFile(string currentFilePath, string newName)
{
var fullFilePath = GetFullFilePath(currentFilePath);
if (!string.IsNullOrWhiteSpace(fullFilePath))
{
try
{
var originalFileName = Path.GetFileNameWithoutExtension(fullFilePath);
var newFilePath = fullFilePath.Replace(originalFileName, newName);
File.Move(fullFilePath, newFilePath);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return false;
}
}
return false;
}
public string GetFullFilePath(string currentFilePath, bool mustExist = true)
{
if (currentFilePath.StartsWith("/"))
@@ -132,9 +168,9 @@ namespace CarCareTracker.Helper
if (!Directory.Exists(tempPath))
Directory.CreateDirectory(tempPath);
int fileIndex = 0;
foreach(GenericReportModel reportModel in exportData)
foreach (GenericReportModel reportModel in exportData)
{
foreach(UploadedFiles file in reportModel.Files)
foreach (UploadedFiles file in reportModel.Files)
{
var fileToCopy = GetFullFilePath(file.Location);
var destFileName = $"{tempPath}/{fileIndex}{Path.GetExtension(file.Location)}";

View File

@@ -11,21 +11,23 @@ namespace CarCareTracker.Helper
{
public string GetAverageGasMileage(List<GasRecordViewModel> results, bool useMPG)
{
var recordsToCalculateGallons = results.Where(x => x.MilesPerGallon > 0 || !x.IsFillToFull);
var recordWithCalculatedMPG = recordsToCalculateGallons.Where(x => x.MilesPerGallon > 0);
var mileageAdjustment = results.Where(x => x.MissedFuelUp).Sum(y => y.DeltaMileage);
if (recordWithCalculatedMPG.Any())
var recordsToCalculate = results.Where(x => x.IncludeInAverage);
if (recordsToCalculate.Any())
{
var maxMileage = recordWithCalculatedMPG.Max(x => x.Mileage);
var minMileage = recordWithCalculatedMPG.Min(x => x.Mileage);
var totalGallonsConsumed = recordsToCalculateGallons.Sum(x => x.Gallons);
var deltaMileage = maxMileage - minMileage - mileageAdjustment;
var averageGasMileage = (deltaMileage) / totalGallonsConsumed;
if (!useMPG)
try
{
var totalMileage = recordsToCalculate.Sum(x => x.DeltaMileage);
var totalGallons = recordsToCalculate.Sum(x => x.Gallons);
var averageGasMileage = totalMileage / totalGallons;
if (!useMPG && averageGasMileage > 0)
{
averageGasMileage = 100 / averageGasMileage;
}
return averageGasMileage.ToString("F");
} catch (Exception ex)
{
return "0";
}
}
return "0";
}
@@ -68,7 +70,8 @@ namespace CarCareTracker.Helper
CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0,
IsFillToFull = currentObject.IsFillToFull,
MissedFuelUp = currentObject.MissedFuelUp,
Notes = currentObject.Notes
Notes = currentObject.Notes,
Tags = currentObject.Tags
};
if (currentObject.MissedFuelUp)
{
@@ -120,7 +123,8 @@ namespace CarCareTracker.Helper
CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0,
IsFillToFull = currentObject.IsFillToFull,
MissedFuelUp = currentObject.MissedFuelUp,
Notes = currentObject.Notes
Notes = currentObject.Notes,
Tags = currentObject.Tags
});
}
previousMileage = currentObject.Mileage;

View File

@@ -0,0 +1,62 @@
using CarCareTracker.Models;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;
namespace CarCareTracker.Helper
{
public interface ITranslationHelper
{
string Translate(string userLanguage, string text);
}
public class TranslationHelper : ITranslationHelper
{
private readonly IFileHelper _fileHelper;
private readonly IConfiguration _config;
private IMemoryCache _cache;
public TranslationHelper(IFileHelper fileHelper, IConfiguration config, IMemoryCache memoryCache)
{
_fileHelper = fileHelper;
_config = config;
_cache = memoryCache;
}
public string Translate(string userLanguage, string text)
{
bool create = bool.Parse(_config["LUBELOGGER_TRANSLATOR"] ?? "false");
//transform input text into key.
string translationKey = text.Replace(" ", "_");
var translationFilePath = userLanguage == "en_US" ? _fileHelper.GetFullFilePath($"/defaults/en_US.json") : _fileHelper.GetFullFilePath($"/translations/{userLanguage}.json", false);
var dictionary = _cache.GetOrCreate<Dictionary<string, string>>($"lang_{userLanguage}", entry =>
{
entry.SlidingExpiration = TimeSpan.FromHours(1);
if (File.Exists(translationFilePath))
{
try
{
var translationFile = File.ReadAllText(translationFilePath);
var translationDictionary = JsonSerializer.Deserialize<Dictionary<string, string>>(translationFile);
return translationDictionary ?? new Dictionary<string, string>();
} catch (Exception ex)
{
return new Dictionary<string, string>();
}
}
else
{
return new Dictionary<string, string>();
}
});
if (dictionary != null && dictionary.ContainsKey(translationKey))
{
return dictionary[translationKey];
}
else if (create && File.Exists(translationFilePath))
{
//create entry
dictionary.Add(translationKey, text);
File.WriteAllText(translationFilePath, JsonSerializer.Serialize(dictionary));
return text;
}
return text;
}
}
}

View File

@@ -25,6 +25,7 @@ namespace CarCareTracker.MapProfile
Map(m => m.Progress).Name(["progress"]);
Map(m => m.Type).Name(["type"]);
Map(m => m.Priority).Name(["priority"]);
Map(m => m.Tags).Name(["tags"]);
}
}
}

View File

@@ -157,7 +157,13 @@ namespace CarCareTracker.Middleware
return Task.CompletedTask;
}
}
if (Request.Path.Value == "/Vehicle/Index" && Request.QueryString.HasValue)
{
Response.Redirect($"/Login/Index?redirectURL={Request.Path.Value}{Request.QueryString.Value}");
} else
{
Response.Redirect("/Login/Index");
}
return Task.CompletedTask;
}
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)

View File

@@ -18,5 +18,6 @@
public bool MissedFuelUp { get; set; } = false;
public string Notes { get; set; }
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
public List<string> Tags { get; set; } = new List<string>();
}
}

View File

@@ -18,6 +18,7 @@
public bool MissedFuelUp { get; set; } = false;
public string Notes { get; set; }
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
public List<string> Tags { get; set; } = new List<string>();
public GasRecord ToGasRecord() { return new GasRecord {
Id = Id,
Cost = Cost,
@@ -28,7 +29,8 @@
Files = Files,
IsFillToFull = IsFillToFull,
MissedFuelUp = MissedFuelUp,
Notes = Notes
Notes = Notes,
Tags = Tags
}; }
}
}

View File

@@ -21,5 +21,7 @@
public bool IsFillToFull { get; set; }
public bool MissedFuelUp { get; set; }
public string Notes { get; set; }
public List<string> Tags { get; set; } = new List<string>();
public bool IncludeInAverage { get { return MilesPerGallon > 0 || (!IsFillToFull && !MissedFuelUp); } }
}
}

View File

@@ -23,6 +23,7 @@
public string PartNumber { get; set; }
public string PartSupplier { get; set; }
public string PartQuantity { get; set; }
public string Tags { get; set; }
}
public class SupplyRecordExportModel
@@ -43,12 +44,14 @@
public string Description { get; set; }
public string Notes { get; set; }
public string Cost { get; set; }
public string Tags { get; set; }
}
public class OdometerRecordExportModel
{
public string Date { get; set; }
public string Odometer { get; set; }
public string Notes { get; set; }
public string Tags { get; set; }
}
public class TaxRecordExportModel
{
@@ -56,6 +59,7 @@
public string Description { get; set; }
public string Notes { get; set; }
public string Cost { get; set; }
public string Tags { get; set; }
}
public class GasRecordExportModel
{
@@ -67,6 +71,7 @@
public string IsFillToFull { get; set; }
public string MissedFuelUp { get; set; }
public string Notes { get; set; }
public string Tags { get; set; }
}
public class ReminderExportModel
{

View File

@@ -8,5 +8,6 @@
public string NoteText { get; set; }
public bool Pinned { 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 SettingsViewModel
{
public UserConfig UserConfig { get; set; }
public List<string> UILanguages { get; set; }
}
}

View File

@@ -17,6 +17,7 @@
public string PreferredGasMileageUnit { get; set; } = string.Empty;
public string UserNameHash { get; set; }
public string UserPasswordHash { get; set;}
public string UserLanguage { get; set; } = "en_US";
public List<ImportMode> VisibleTabs { get; set; } = new List<ImportMode>() {
ImportMode.Dashboard,
ImportMode.ServiceRecord,

View File

@@ -5,6 +5,8 @@ using CarCareTracker.Logic;
using CarCareTracker.Middleware;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args);
@@ -33,6 +35,7 @@ builder.Services.AddSingleton<IReminderHelper, ReminderHelper>();
builder.Services.AddSingleton<IReportHelper, ReportHelper>();
builder.Services.AddSingleton<IMailHelper, MailHelper>();
builder.Services.AddSingleton<IConfigHelper, ConfigHelper>();
builder.Services.AddSingleton<ITranslationHelper, TranslationHelper>();
//configure logic
builder.Services.AddSingleton<ILoginLogic, LoginLogic>();
@@ -54,6 +57,17 @@ builder.Services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes("AuthN").RequireAuthenticatedUser().Build();
});
//Configure max file upload size
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = int.MaxValue; // if don't set default value is: 30 MB
});
builder.Services.Configure<FormOptions>(options =>
{
options.ValueLengthLimit = int.MaxValue;
options.MultipartBodyLengthLimit = int.MaxValue; // if don't set default value is: 128 MB
options.MultipartHeadersLengthLimit = int.MaxValue;
});
var app = builder.Build();

View File

@@ -1,10 +1,13 @@
@{
@using CarCareTracker.Helper
@{
ViewData["Title"] = "Admin";
}
@inject IConfiguration config;
@inject ITranslationHelper translator
@{
bool emailServerIsSetup = true;
var mailConfig = config.GetSection("MailConfig").Get<MailConfig>();
var userLanguage = config[nameof(UserConfig.UserLanguage)] ?? "en_US";
if (mailConfig is null || string.IsNullOrWhiteSpace(mailConfig.EmailServer))
{
emailServerIsSetup = false;
@@ -17,31 +20,31 @@
<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">
<span class="display-6">Admin Panel</span>
<span class="display-6">@translator.Translate(userLanguage, "Admin Panel")</span>
</div>
</div>
<hr />
<div class="row">
<div class="col-md-5 col-12">
<span class="lead">Tokens</span>
<span class="lead">@translator.Translate(userLanguage, "Tokens")</span>
<hr />
<div class="row">
<div class="col-6">
<button onclick="generateNewToken()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Generate User Token</button>
<button onclick="generateNewToken()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Generate User Token")</button>
</div>
<div class="col-6 d-flex align-items-center">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="enableAutoNotify" @(emailServerIsSetup ? "checked" : "disabled")>
<label class="form-check-label" for="enableAutoNotify">Auto Notify(via Email)</label>
<label class="form-check-label" for="enableAutoNotify">@translator.Translate(userLanguage, "Auto Notify(via Email)")</label>
</div>
</div>
</div>
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-4">Token</th>
<th scope="col" class="col-6">Issued To</th>
<th scope="col" class="col-2">Delete</th>
<th scope="col" class="col-4">@translator.Translate(userLanguage, "Token")</th>
<th scope="col" class="col-6">@translator.Translate(userLanguage, "Issued To")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Delete")</th>
</tr>
</thead>
<tbody>
@@ -59,15 +62,15 @@
</table>
</div>
<div class="col-12 col-md-7">
<span class="lead">Users</span>
<span class="lead">@translator.Translate(userLanguage, "Users")</span>
<hr />
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-4">Username</th>
<th scope="col" class="col-4">Email</th>
<th scope="col" class="col-2">Is Admin</th>
<th scope="col" class="col-2">Delete</th>
<th scope="col" class="col-4">@translator.Translate(userLanguage, "Username")</th>
<th scope="col" class="col-4">@translator.Translate(userLanguage, "Email")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Is Admin")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Delete")</th>
</tr>
</thead>
<tbody>
@@ -92,7 +95,7 @@
if (data){
reloadPage();
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}
@@ -104,7 +107,7 @@
if (data) {
reloadPage();
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}
@@ -113,7 +116,7 @@
if (data) {
reloadPage();
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}

View File

@@ -1,7 +1,10 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableAuth = config.GetUserConfig(User).EnableAuth;
var userConfig = config.GetUserConfig(User);
var enableAuth = userConfig.EnableAuth;
var userLanguage = userConfig.UserLanguage;
var logoUrl = config.GetLogoUrl();
}
@model string
@@ -14,21 +17,21 @@
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
<ul class="navbar-nav" id="homeTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link user-select-none @(Model == "garage" ? "active" : "")" ontouchstart="detectLongTouch(this)" ontouchend="detectTouchEndPremature(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-car-front me-2"></i>Garage</span></button>
<button class="nav-link user-select-none @(Model == "garage" ? "active" : "")" ontouchstart="detectLongTouch(this)" ontouchend="detectTouchEndPremature(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-car-front me-2"></i>@translator.Translate(userLanguage,"Garage")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-gear me-2"></i>Settings</span></button>
<button class="nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-gear me-2"></i>@translator.Translate(userLanguage,"Settings")</span></button>
</li>
@if (User.IsInRole("CookieAuth"))
{
@if (User.IsInRole(nameof(UserData.IsAdmin)))
{
<li class="nav-item" role="presentation">
<a class="dropdown-item" href="/Admin"><span class="display-3 ms-2"><i class="bi bi-people me-2"></i>Admin Panel</span></a>
<a class="dropdown-item" href="/Admin"><span class="display-3 ms-2"><i class="bi bi-people me-2"></i>@translator.Translate(userLanguage,"Admin Panel")</span></a>
</li>
}
<li class="nav-item" role="presentation">
<button class="nav-link" onclick="performLogOut()"><span class="display-3 ms-2"><i class="bi bi-box-arrow-right me-2"></i>Logout</span></button>
<button class="nav-link" onclick="performLogOut()"><span class="display-3 ms-2"><i class="bi bi-box-arrow-right me-2"></i>@translator.Translate(userLanguage,"Logout")</span></button>
</li>
}
</ul>
@@ -45,10 +48,10 @@
<hr />
<ul class="nav nav-tabs lubelogger-tab" id="homeTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link @(Model == "garage" ? "active" : "")" oncontextmenu="sortGarage(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><i class="bi bi-car-front me-2"></i>Garage</button>
<button class="nav-link @(Model == "garage" ? "active" : "")" oncontextmenu="sortGarage(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><i class="bi bi-car-front me-2"></i>@translator.Translate(userLanguage,"Garage")</button>
</li>
<li class="nav-item ms-auto" role="presentation">
<button class="nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><i class="bi bi-gear me-2"></i>Settings</button>
<button class="nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><i class="bi bi-gear me-2"></i>@translator.Translate(userLanguage,"Settings")</button>
</li>
@if (User.IsInRole("CookieAuth"))
{
@@ -58,11 +61,11 @@
@if (User.IsInRole(nameof(UserData.IsAdmin)))
{
<li>
<a class="dropdown-item" href="/Admin"><i class="bi bi-people me-2"></i>Admin Panel</a>
<a class="dropdown-item" href="/Admin"><i class="bi bi-people me-2"></i>@translator.Translate(userLanguage,"Admin Panel")</a>
</li>
}
<li>
<button class="dropdown-item" onclick="performLogOut()"><i class="bi bi-box-arrow-right me-2"></i>Logout</button>
<button class="dropdown-item" onclick="performLogOut()"><i class="bi bi-box-arrow-right me-2"></i>@translator.Translate(userLanguage,"Logout")</button>
</li>
</ul>
</li>

View File

@@ -1,158 +1,185 @@
@using CarCareTracker.Helper
@model UserConfig
@model SettingsViewModel
@inject ITranslationHelper translator
@{
var userLanguage = Model.UserConfig.UserLanguage;
}
<div class="row">
<div class="d-flex justify-content-center">
<h6 class="display-6 mt-2">Settings</h6>
<h6 class="display-6 mt-2">@translator.Translate(userLanguage, "Settings")</h6>
</div>
<hr />
<div class="col-12 col-md-6">
<input id="preferredGasUnit" style="display:none;" value="@Model.PreferredGasUnit"/>
<input id="preferredFuelMileageUnit" style="display:none;" value="@Model.PreferredGasMileageUnit" />
<input id="preferredGasUnit" style="display:none;" value="@Model.UserConfig.PreferredGasUnit" />
<input id="preferredFuelMileageUnit" style="display:none;" value="@Model.UserConfig.PreferredGasMileageUnit" />
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableDarkMode" checked="@Model.UseDarkMode">
<label class="form-check-label" for="enableDarkMode">Dark Mode</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableDarkMode" checked="@Model.UserConfig.UseDarkMode">
<label class="form-check-label" for="enableDarkMode">@translator.Translate(userLanguage, "Dark Mode")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableCsvImports" checked="@Model.EnableCsvImports">
<label class="form-check-label" for="enableCsvImports">Enable CSV Imports</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableCsvImports" checked="@Model.UserConfig.EnableCsvImports">
<label class="form-check-label" for="enableCsvImports">@translator.Translate(userLanguage, "Enable CSV Imports")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMPG" checked="@Model.UseMPG">
<label class="form-check-label" for="useMPG">Use Imperial Calculation for Fuel Economy Calculations(MPG)<br /><small class="text-body-secondary">This Will Also Change Units to Miles and Gallons</small></label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMPG" checked="@Model.UserConfig.UseMPG">
<label class="form-check-label" for="useMPG">@translator.Translate(userLanguage, "Use Imperial Calculation for Fuel Economy Calculations(MPG)")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "This Will Also Change Units to Miles and Gallons")</small></label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useUKMPG" checked="@Model.UseUKMPG">
<label class="form-check-label" for="useUKMPG">Use UK MPG Calculation<br /><small class="text-body-secondary">Input Gas Consumption in Liters, it will be converted to UK Gals for MPG Calculation</small></label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useUKMPG" checked="@Model.UserConfig.UseUKMPG">
<label class="form-check-label" for="useUKMPG">@translator.Translate(userLanguage, "Use UK MPG Calculation")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Input Gas Consumption in Liters, it will be converted to UK Gals for MPG Calculation")</small></label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useDescending" checked="@Model.UseDescending">
<label class="form-check-label" for="useDescending">Sort lists in Descending Order(Newest to Oldest)</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useDescending" checked="@Model.UserConfig.UseDescending">
<label class="form-check-label" for="useDescending">@translator.Translate(userLanguage, "Sort lists in Descending Order(Newest to Oldest)")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="hideZero" checked="@Model.HideZero">
<label class="form-check-label" for="hideZero">Replace @(0.ToString("C")) Costs with ---</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="hideZero" checked="@Model.UserConfig.HideZero">
<label class="form-check-label" for="hideZero">@translator.Translate(userLanguage, "Replace $0.00 Costs with ---")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimal" checked="@Model.UseThreeDecimalGasCost">
<label class="form-check-label" for="useThreeDecimal">Use Three Decimals For Fuel Cost</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimal" checked="@Model.UserConfig.UseThreeDecimalGasCost">
<label class="form-check-label" for="useThreeDecimal">@translator.Translate(userLanguage, "Use Three Decimals For Fuel Cost")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMarkDownOnSavedNotes" checked="@Model.UseMarkDownOnSavedNotes">
<label class="form-check-label" for="useMarkDownOnSavedNotes">Display Saved Notes in Markdown</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMarkDownOnSavedNotes" checked="@Model.UserConfig.UseMarkDownOnSavedNotes">
<label class="form-check-label" for="useMarkDownOnSavedNotes">@translator.Translate(userLanguage, "Display Saved Notes in Markdown")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoReminderRefresh" checked="@Model.EnableAutoReminderRefresh">
<label class="form-check-label" for="enableAutoReminderRefresh">Auto Refresh Lapsed Recurring Reminders</label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoReminderRefresh" checked="@Model.UserConfig.EnableAutoReminderRefresh">
<label class="form-check-label" for="enableAutoReminderRefresh">@translator.Translate(userLanguage, "Auto Refresh Lapsed Recurring Reminders")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoOdometerInsert" checked="@Model.EnableAutoOdometerInsert">
<label class="form-check-label" for="enableAutoOdometerInsert">Auto Insert Odometer Records<br /><small class="text-body-secondary">Only when Adding Service/Repair/Upgrade/Fuel Record or Completing a Plan</small></label>
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoOdometerInsert" checked="@Model.UserConfig.EnableAutoOdometerInsert">
<label class="form-check-label" for="enableAutoOdometerInsert">@translator.Translate(userLanguage, "Auto Insert Odometer Records")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Only when Adding Service/Repair/Upgrade/Fuel Record or Completing a Plan")</small></label>
</div>
@if (User.IsInRole(nameof(UserData.IsRootUser)))
{
<div class="form-check form-switch">
<input class="form-check-input" onChange="enableAuthCheckChanged()" type="checkbox" role="switch" id="enableAuth" checked="@Model.EnableAuth">
<label class="form-check-label" for="enableAuth">Enable Authentication</label>
<input class="form-check-input" onChange="enableAuthCheckChanged()" type="checkbox" role="switch" id="enableAuth" checked="@Model.UserConfig.EnableAuth">
<label class="form-check-label" for="enableAuth">@translator.Translate(userLanguage, "Enable Authentication")</label>
</div>
}
</div>
<div class="col-12 col-md-6">
<div class="row" id="visibleTabs">
<div class="col-12">
<span class="lead">Visible Tabs</span>
<span class="lead">@translator.Translate(userLanguage, "Visible Tabs")</span>
</div>
<div class="col-12 col-md-6">
<ul class="list-group">
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="ServiceRecord" id="serviceRecordTab" @(Model.VisibleTabs.Contains(ImportMode.ServiceRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="serviceRecordTab">Service Records</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="ServiceRecord" id="serviceRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.ServiceRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="serviceRecordTab">@translator.Translate(userLanguage, "Service Records")</label>
</li>
<li class="list-group-item d-none">
<input onChange="updateSettings()" disabled class="form-check-input me-1" type="checkbox" value="Dashboard" id="dashboardTab" @(Model.VisibleTabs.Contains(ImportMode.Dashboard) ? "checked" : "")>
<label class="form-check-label stretched-link" for="dashboardTab">Dashboard</label>
<input onChange="updateSettings()" disabled class="form-check-input me-1" type="checkbox" value="Dashboard" id="dashboardTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.Dashboard) ? "checked" : "")>
<label class="form-check-label stretched-link" for="dashboardTab">@translator.Translate(userLanguage, "Dashboard")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="RepairRecord" id="repairRecordTab" @(Model.VisibleTabs.Contains(ImportMode.RepairRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="repairRecordTab">Repairs</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="RepairRecord" id="repairRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.RepairRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="repairRecordTab">@translator.Translate(userLanguage, "Repairs")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="UpgradeRecord" id="upgradeRecordTab" @(Model.VisibleTabs.Contains(ImportMode.UpgradeRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="upgradeRecordTab">Upgrades</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="UpgradeRecord" id="upgradeRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.UpgradeRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="upgradeRecordTab">@translator.Translate(userLanguage, "Upgrades")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="GasRecord" id="gasRecordTab" @(Model.VisibleTabs.Contains(ImportMode.GasRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="gasRecordTab">Fuel</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="GasRecord" id="gasRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.GasRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="gasRecordTab">@translator.Translate(userLanguage, "Fuel")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="OdometerRecord" id="odometerRecordTab" @(Model.VisibleTabs.Contains(ImportMode.OdometerRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="odometerRecordTab">Odometer</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="OdometerRecord" id="odometerRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.OdometerRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="odometerRecordTab">@translator.Translate(userLanguage, "Odometer")</label>
</li>
</ul>
</div>
<div class="col-12 col-md-6">
<ul class="list-group">
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="TaxRecord" id="taxRecordTab" @(Model.VisibleTabs.Contains(ImportMode.TaxRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="taxRecordTab">Taxes</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="TaxRecord" id="taxRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.TaxRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="taxRecordTab">@translator.Translate(userLanguage, "Taxes")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="NoteRecord" id="noteRecordTab" @(Model.VisibleTabs.Contains(ImportMode.NoteRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="noteRecordTab">Notes</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="NoteRecord" id="noteRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.NoteRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="noteRecordTab">@translator.Translate(userLanguage, "Notes")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="ReminderRecord" id="reminderRecordTab" @(Model.VisibleTabs.Contains(ImportMode.ReminderRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="reminderRecordTab">Reminder</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="ReminderRecord" id="reminderRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.ReminderRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="reminderRecordTab">@translator.Translate(userLanguage, "Reminder")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="SupplyRecord" id="supplyRecordTab" @(Model.VisibleTabs.Contains(ImportMode.SupplyRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="supplyRecordTab">Supplies</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="SupplyRecord" id="supplyRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.SupplyRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="supplyRecordTab">@translator.Translate(userLanguage, "Supplies")</label>
</li>
<li class="list-group-item">
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="PlanRecord" id="planRecordTab" @(Model.VisibleTabs.Contains(ImportMode.PlanRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="planRecordTab">Planner</label>
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="PlanRecord" id="planRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.PlanRecord) ? "checked" : "")>
<label class="form-check-label stretched-link" for="planRecordTab">@translator.Translate(userLanguage, "Planner")</label>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-12 col-md-6">
<span class="lead">Default Tab</span>
<span class="lead">@translator.Translate(userLanguage, "Default Tab")</span>
<select class="form-select" onchange="updateSettings()" id="defaultTab">
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.Dashboard)) value="Dashboard">Dashboard</!option>
<!option @(StaticHelper.DefaultTabSelected(Model,ImportMode.ServiceRecord)) value="ServiceRecord">Service Record</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.RepairRecord)) value="RepairRecord">Repairs</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.UpgradeRecord)) value="UpgradeRecord">Upgrades</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.GasRecord)) value="GasRecord">Fuel</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.TaxRecord)) value="TaxRecord">Tax</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.NoteRecord)) value="NoteRecord">Notes</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.ReminderRecord)) value="ReminderRecord">Reminders</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.SupplyRecord)) value="SupplyRecord">Supplies</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.PlanRecord)) value="PlanRecord">Planner</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.OdometerRecord)) value="OdometerRecord">Odometer</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.Dashboard)) value="Dashboard">@translator.Translate(userLanguage, "Dashboard")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.ServiceRecord)) value="ServiceRecord">@translator.Translate(userLanguage, "Service Record")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.RepairRecord)) value="RepairRecord">@translator.Translate(userLanguage, "Repairs")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.UpgradeRecord)) value="UpgradeRecord">@translator.Translate(userLanguage, "Upgrades")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.GasRecord)) value="GasRecord">@translator.Translate(userLanguage, "Fuel")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.TaxRecord)) value="TaxRecord">@translator.Translate(userLanguage, "Tax")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.NoteRecord)) value="NoteRecord">@translator.Translate(userLanguage, "Notes")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.ReminderRecord)) value="ReminderRecord">@translator.Translate(userLanguage, "Reminders")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.SupplyRecord)) value="SupplyRecord">@translator.Translate(userLanguage, "Supplies")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.PlanRecord)) value="PlanRecord">@translator.Translate(userLanguage, "Planner")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.OdometerRecord)) value="OdometerRecord">@translator.Translate(userLanguage, "Odometer")</!option>
</select>
</div>
<div class="col-12 col-md-6">
<span class="lead">@translator.Translate(userLanguage, "Language")</span>
<select class="form-select" onchange="updateSettings()" id="defaultLanguage">
@foreach (string uiLanguage in Model.UILanguages)
{
<!option @(Model.UserConfig.UserLanguage == uiLanguage ? "selected" : "")>@uiLanguage</!option>
}
</select>
</div>
</div>
@if (User.IsInRole(nameof(UserData.IsRootUser)))
{
<div class="row">
<div class="col-12 col-md-6">
<span class="lead">Backups</span>
<span class="lead">@translator.Translate(userLanguage, "Backups")</span>
<div class="row">
<div class="col-6 d-grid">
<button onclick="makeBackup()" class="btn btn-primary btn-md">Make</button>
<button onclick="makeBackup()" class="btn btn-primary btn-md">@translator.Translate(userLanguage, "Make")</button>
</div>
<div class="col-6 d-grid">
<input onChange="restoreBackup(this)" type="file" accept=".zip" class="d-none" id="inputBackup">
<button onclick="openRestoreBackup()" class="btn btn-secondary btn-md">Restore</button>
<button onclick="openRestoreBackup()" class="btn btn-secondary btn-md">@translator.Translate(userLanguage, "Restore")</button>
</div>
</div>
</div>
<div class="col-12 col-md-6">
<span class="lead">@translator.Translate(userLanguage, "Manage Languages")</span>
<div class="row">
<div class="col-6 d-grid">
<input onChange="uploadLanguage(this)" type="file" accept=".json" class="d-none" id="inputLanguage">
<button onclick="openUploadLanguage()" class="btn btn-primary btn-md">@translator.Translate(userLanguage, "Upload")</button>
</div>
<div class="col-6 d-grid">
<button onclick="deleteLanguage()" @(Model.UserConfig.UserLanguage == "en_US" ? "disabled" : "") class="btn btn-danger btn-md">@translator.Translate(userLanguage, "Delete")</button>
</div>
</div>
</div>
</div>
}
</div>
</div>
</div>
<div class="row">
<div class="d-flex justify-content-center">
<h6 class="display-6 mt-2">About</h6>
<h6 class="display-6 mt-2">@translator.Translate(userLanguage, "About")</h6>
</div>
<hr />
<div class="col-12 col-md-6">
@@ -204,10 +231,18 @@
});
return visibleTabs.toArray();
}
function updateSettings(){
function deleteLanguage() {
var languageFileLocation = `/translations/${$("#defaultLanguage").val()}.json`;
$.post('/Files/DeleteFiles', { fileLocation: languageFileLocation }, function (data) {
//reset user language back to en_US
$("#defaultLanguage").val('en_US');
updateSettings();
});
}
function updateSettings() {
var visibleTabs = getCheckedTabs();
var defaultTab = $("#defaultTab").val();
if (!visibleTabs.includes(defaultTab)){
if (!visibleTabs.includes(defaultTab)) {
defaultTab = "Dashboard"; //default to dashboard.
}
var userConfigObject = {
@@ -223,16 +258,17 @@
enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"),
preferredGasUnit: $("#preferredGasUnit").val(),
preferredGasMileageUnit: $("#preferredFuelMileageUnit").val(),
userLanguage: $("#defaultLanguage").val(),
visibleTabs: visibleTabs,
defaultTab: defaultTab
}
sloader.show();
$.post('/Home/WriteToSettings', { userConfig: userConfigObject}, function(data){
$.post('/Home/WriteToSettings', { userConfig: userConfigObject }, function (data) {
sloader.hide();
if (data) {
setTimeout(function () { window.location.href = '/Home/Index?tab=settings' }, 500);
} else {
errorToast("An error occurred, please try again later.")
errorToast(genericErrorMessage());
}
})
}
@@ -241,9 +277,37 @@
window.location.href = data;
});
}
function openRestoreBackup(){
function openUploadLanguage(){
$("#inputLanguage").click();
}
function openRestoreBackup() {
$("#inputBackup").click();
}
function uploadLanguage(event){
let formData = new FormData();
formData.append("file", event.files[0]);
sloader.show();
$.ajax({
url: "/Files/HandleTranslationFileUpload",
data: formData,
cache: false,
processData: false,
contentType: false,
type: 'POST',
success: function (response) {
sloader.hide();
if (response.success) {
setTimeout(function () { window.location.href = '/Home/Index?tab=settings' }, 500);
} else {
errorToast(response.message);
}
},
error: function(){
sloader.hide();
errorToast("An error has occurred, please check the file size and try again later.");
}
});
}
function restoreBackup(event) {
let formData = new FormData();
formData.append("file", event.files[0]);
@@ -257,20 +321,24 @@
type: 'POST',
success: function (response) {
if (response.trim() != '') {
$.post('/Files/RestoreBackup', { fileName : response}, function (data) {
$.post('/Files/RestoreBackup', { fileName: response }, function (data) {
sloader.hide();
if (data){
if (data) {
successToast("Backup Restored");
setTimeout(function () { window.location.href = '/Home/Index' }, 500);
} else {
errorToast("An error occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}
},
error: function () {
sloader.hide();
errorToast("An error has occurred, please check the file size and try again later.");
}
});
}
function enableAuthCheckChanged(){
function enableAuthCheckChanged() {
var enableAuth = $("#enableAuth").is(":checked");
if (enableAuth) {
//swal dialog to set up username and password.
@@ -296,7 +364,7 @@
if (data) {
setTimeout(function () { window.location.href = '/Login' }, 500);
} else {
errorToast("An error occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -306,7 +374,7 @@
if (data) {
setTimeout(function () { window.location.href = '/Home' }, 1000);
} else {
errorToast("An error occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}

View File

@@ -1,7 +1,9 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
}
@{
ViewData["Title"] = "LubeLogger - Login";
@@ -14,17 +16,17 @@
<div class="col-12">
<img src="@logoUrl" />
<div class="form-group">
<label for="inputUserName">Username</label>
<label for="inputUserName">@translator.Translate(userLanguage, "Username")</label>
<input type="text" id="inputUserName" class="form-control">
</div>
<div class="d-grid">
<button type="button" class="btn btn-warning mt-2" onclick="requestPasswordReset()"><i class="bi bi-box-arrow-in-right me-2"></i>Request</button>
<button type="button" class="btn btn-warning mt-2" onclick="requestPasswordReset()"><i class="bi bi-box-arrow-in-right me-2"></i>@translator.Translate(userLanguage, "Request")</button>
</div>
<div class="d-grid">
<a href="/Login/ResetPassword" class="btn btn-link mt-2">I Have a Token</a>
<a href="/Login/ResetPassword" class="btn btn-link mt-2">@translator.Translate(userLanguage, "I Have a Token")</a>
</div>
<div class="d-grid">
<a href="/Login/Index" class="btn btn-link mt-2">Back to Login</a>
<a href="/Login/Index" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Back to Login")</a>
</div>
</div>
</div>

View File

@@ -1,7 +1,10 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model string
@{
var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
}
@{
ViewData["Title"] = "LubeLogger - Login";
@@ -14,26 +17,31 @@
<div class="col-12">
<img src="@logoUrl" />
<div class="form-group">
<label for="inputUserName">Username</label>
<label for="inputUserName">@translator.Translate(userLanguage, "Username")</label>
<input type="text" id="inputUserName" class="form-control">
</div>
<div class="form-group">
<label for="inputUserPassword">Password</label>
<label for="inputUserPassword">@translator.Translate(userLanguage, "Password")</label>
<input type="password" id="inputUserPassword" onkeyup="handlePasswordKeyPress(event)" class="form-control">
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="inputPersistent">
<label class="form-check-label" for="inputPersistent">Remember Me</label>
<label class="form-check-label" for="inputPersistent">@translator.Translate(userLanguage, "Remember Me")</label>
</div>
<div class="d-grid">
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>Login</button>
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>@translator.Translate(userLanguage, "Login")</button>
</div>
<div class="d-grid">
<a href="/Login/ForgotPassword" class="btn btn-link mt-2">Forgot Password</a>
<a href="/Login/ForgotPassword" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Forgot Password")</a>
</div>
<div class="d-grid">
<a href="/Login/Registration" class="btn btn-link mt-2">Register</a>
<a href="/Login/Registration" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Register")</a>
</div>
</div>
</div>
</div>
<script>
function getRedirectURL(){
return { url: decodeHTMLEntities('@Model') };
}
</script>

View File

@@ -1,7 +1,9 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
}
@{
ViewData["Title"] = "LubeLogger - Register";
@@ -14,26 +16,26 @@
<div class="col-12">
<img src="@logoUrl" />
<div class="form-group">
<label for="inputToken">Token</label>
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
<input type="text" id="inputToken" class="form-control">
</div>
<div class="form-group">
<label for="inputUserName">Email Address</label>
<label for="inputUserName">@translator.Translate(userLanguage, "Email Address")</label>
<input type="text" id="inputEmail" class="form-control">
</div>
<div class="form-group">
<label for="inputUserName">Username</label>
<label for="inputUserName">@translator.Translate(userLanguage, "Username")</label>
<input type="text" id="inputUserName" class="form-control">
</div>
<div class="form-group">
<label for="inputUserPassword">Password</label>
<label for="inputUserPassword">@translator.Translate(userLanguage, "Password")</label>
<input type="password" id="inputUserPassword" class="form-control">
</div>
<div class="d-grid">
<button type="button" class="btn btn-warning mt-2" onclick="performRegistration()"><i class="bi bi-box-arrow-in-right me-2"></i>Register</button>
<button type="button" class="btn btn-warning mt-2" onclick="performRegistration()"><i class="bi bi-box-arrow-in-right me-2"></i>@translator.Translate(userLanguage, "Register")</button>
</div>
<div class="d-grid">
<a href="/Login/Index" class="btn btn-link mt-2">Back to Login</a>
<a href="/Login/Index" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Back to Login")</a>
</div>
</div>
</div>

View File

@@ -1,7 +1,9 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
}
@{
ViewData["Title"] = "LubeLogger - Register";
@@ -14,22 +16,22 @@
<div class="col-12">
<img src="@logoUrl" />
<div class="form-group">
<label for="inputToken">Token</label>
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
<input type="text" id="inputToken" class="form-control">
</div>
<div class="form-group">
<label for="inputUserName">Email Address</label>
<label for="inputUserName">@translator.Translate(userLanguage, "Email Address")</label>
<input type="text" id="inputEmail" class="form-control">
</div>
<div class="form-group">
<label for="inputUserPassword">New Password</label>
<label for="inputUserPassword">@translator.Translate(userLanguage, "New Password")</label>
<input type="password" id="inputUserPassword" class="form-control">
</div>
<div class="d-grid">
<button type="button" class="btn btn-warning mt-2" onclick="performPasswordReset()"><i class="bi bi-box-arrow-in-right me-2"></i>Reset Password</button>
<button type="button" class="btn btn-warning mt-2" onclick="performPasswordReset()"><i class="bi bi-box-arrow-in-right me-2"></i>@translator.Translate(userLanguage, "Reset Password")</button>
</div>
<div class="d-grid">
<a href="/Login/Index" class="btn btn-link mt-2">Back to Login</a>
<a href="/Login/Index" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Back to Login")</a>
</div>
</div>
</div>

View File

@@ -1 +1,8 @@
<h1>Access Denied</h1>
<div class="row">
<div class="col-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">
<h1>Access Denied</h1>
</div>
</div>

View File

@@ -1,14 +1,17 @@
@using CarCareTracker.Helper
<!DOCTYPE html>
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var useDarkMode = userConfig.UseDarkMode;
var enableCsvImports = userConfig.EnableCsvImports;
var useMPG = userConfig.UseMPG;
var useMarkDown = userConfig.UseMarkDownOnSavedNotes;
var useThreeDecimals = userConfig.UseThreeDecimalGasCost;
var shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var numberFormat = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
var userLanguage = userConfig.UserLanguage;
shortDatePattern = shortDatePattern.ToLower();
if (!shortDatePattern.Contains("dd"))
{
@@ -54,7 +57,8 @@
enableCsvImport : "@enableCsvImports" == "True",
useMarkDown: "@useMarkDown" == "True",
currencySymbol: decodeHTMLEntities("@numberFormat.CurrencySymbol"),
useThreeDecimals: "@useThreeDecimals" == "True"
useThreeDecimals: "@useThreeDecimals" == "True",
useMPG: "@useMPG" == "True"
}
}
function getShortDatePattern() {
@@ -83,6 +87,9 @@
input = input.replace(".", decimalSeparator);
return input;
}
function genericErrorMessage(){
return decodeHTMLEntities('@translator.Translate(userLanguage, "An error has occurred, please try again later")');
}
</script>
@await RenderSectionAsync("Scripts", required: false)
</head>

View File

@@ -3,8 +3,10 @@
ViewData["Title"] = "LubeLogger - View Vehicle";
}
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model Vehicle
@section Scripts {
@@ -26,46 +28,46 @@
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
<ul class="nav navbar-nav" id="vehicleTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" onclick="returnToGarage()"><span class="display-3 ms-2"><i class="bi bi-arrow-left-square"></i>Garage</span></button>
<button class="nav-link" onclick="returnToGarage()"><span class="display-3 ms-2"><i class="bi bi-arrow-left-square me-2"></i>@translator.Translate(userLanguage,"Garage")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" onclick="editVehicle(@Model.Id)"><span class="display-3 ms-2"><i class="bi bi-pencil-square"></i>Edit Vehicle</span></button>
<button class="nav-link" onclick="editVehicle(@Model.Id)"><span class="display-3 ms-2"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Edit Vehicle")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.Dashboard)" id="report-tab" data-bs-toggle="tab" data-bs-target="#report-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-file-bar-graph me-2"></i>Dashboard</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.Dashboard)" id="report-tab" data-bs-toggle="tab" data-bs-target="#report-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-file-bar-graph me-2"></i>@translator.Translate(userLanguage, "Dashboard")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.PlanRecord)" id="plan-tab" data-bs-toggle="tab" data-bs-target="#plan-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-bar-chart-steps me-2"></i>Planner</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.PlanRecord)" id="plan-tab" data-bs-toggle="tab" data-bs-target="#plan-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-bar-chart-steps me-2"></i>@translator.Translate(userLanguage, "Planner")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.OdometerRecord)" id="odometer-tab" data-bs-toggle="tab" data-bs-target="#odometer-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-speedometer me-2"></i>Odometer</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.OdometerRecord)" id="odometer-tab" data-bs-toggle="tab" data-bs-target="#odometer-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-speedometer me-2"></i>@translator.Translate(userLanguage, "Odometer")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab" data-bs-toggle="tab" data-bs-target="#servicerecord-tab-pane" type="button" role="tab" aria-selected="true"><span class="display-3 ms-2"><i class="bi bi-card-checklist me-2"></i>Service Records</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab" data-bs-toggle="tab" data-bs-target="#servicerecord-tab-pane" type="button" role="tab" aria-selected="true"><span class="display-3 ms-2"><i class="bi bi-card-checklist me-2"></i>@translator.Translate(userLanguage, "Service Records")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.RepairRecord)" id="accident-tab" data-bs-toggle="tab" data-bs-target="#accident-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-exclamation-octagon me-2"></i>Repairs</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.RepairRecord)" id="accident-tab" data-bs-toggle="tab" data-bs-target="#accident-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-exclamation-octagon me-2"></i>@translator.Translate(userLanguage,"Repairs")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.UpgradeRecord)" id="upgrade-tab" data-bs-toggle="tab" data-bs-target="#upgrade-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-wrench-adjustable me-2"></i>Upgrades</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.UpgradeRecord)" id="upgrade-tab" data-bs-toggle="tab" data-bs-target="#upgrade-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-wrench-adjustable me-2"></i>@translator.Translate(userLanguage, "Upgrades")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.GasRecord)" id="gas-tab" data-bs-toggle="tab" data-bs-target="#gas-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-fuel-pump me-2"></i>Fuel</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.GasRecord)" id="gas-tab" data-bs-toggle="tab" data-bs-target="#gas-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-fuel-pump me-2"></i>@translator.Translate(userLanguage, "Fuel")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.SupplyRecord)" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-shop me-2"></i>Supplies</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.SupplyRecord)" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-shop me-2"></i>@translator.Translate(userLanguage, "Supplies")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.TaxRecord)" id="tax-tab" data-bs-toggle="tab" data-bs-target="#tax-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-currency-dollar me-2"></i>Taxes</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.TaxRecord)" id="tax-tab" data-bs-toggle="tab" data-bs-target="#tax-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-currency-dollar me-2"></i>@translator.Translate(userLanguage, "Taxes")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.NoteRecord)" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-journal-bookmark me-2"></i>Notes</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.NoteRecord)" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><i class="bi bi-journal-bookmark me-2"></i>@translator.Translate(userLanguage, "Notes")</span></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ReminderRecord)" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><div class="reminderBellDiv" style="display:inline-flex;"><i class="reminderBell bi bi-bell me-2"></i></div>Reminders</span></button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ReminderRecord)" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><span class="display-3 ms-2"><div class="reminderBellDiv" style="display:inline-flex;"><i class="reminderBell bi bi-bell me-2"></i></div>@translator.Translate(userLanguage, "Reminders")</span></button>
</li>
<li class="nav-item" role="presentation">
<button onclick="deleteVehicle(@Model.Id)" class="dropdown-item"><span class="display-3 ms-2"><i class="bi bi-trash me-2"></i>Delete Vehicle</span></button>
<button onclick="deleteVehicle(@Model.Id)" class="dropdown-item"><span class="display-3 ms-2"><i class="bi bi-trash me-2"></i>@translator.Translate(userLanguage, "Delete Vehicle")</span></button>
</li>
</ul>
</div>
@@ -83,42 +85,42 @@
<hr />
<ul class="nav nav-tabs lubelogger-tab" id="vehicleTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.Dashboard)" id="report-tab" data-bs-toggle="tab" data-bs-target="#report-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-file-bar-graph me-2"></i>Dash</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.Dashboard)" id="report-tab" data-bs-toggle="tab" data-bs-target="#report-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-file-bar-graph me-2"></i>@translator.Translate(userLanguage,"Dashboard")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.PlanRecord)" id="plan-tab" data-bs-toggle="tab" data-bs-target="#plan-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-bar-chart-steps me-2"></i>Planner</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.PlanRecord)" id="plan-tab" data-bs-toggle="tab" data-bs-target="#plan-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-bar-chart-steps me-2"></i>@translator.Translate(userLanguage, "Planner")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.OdometerRecord)" id="odometer-tab" data-bs-toggle="tab" data-bs-target="#odometer-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-speedometer me-2"></i>Odometer</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.OdometerRecord)" id="odometer-tab" data-bs-toggle="tab" data-bs-target="#odometer-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-speedometer me-2"></i>@translator.Translate(userLanguage, "Odometer")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab" data-bs-toggle="tab" data-bs-target="#servicerecord-tab-pane" type="button" role="tab" aria-selected="true"><i class="bi bi-card-checklist me-2"></i>Service Records</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab" data-bs-toggle="tab" data-bs-target="#servicerecord-tab-pane" type="button" role="tab" aria-selected="true"><i class="bi bi-card-checklist me-2"></i>@translator.Translate(userLanguage, "Service Records")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.RepairRecord)" id="accident-tab" data-bs-toggle="tab" data-bs-target="#accident-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-exclamation-octagon me-2"></i>Repairs</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.RepairRecord)" id="accident-tab" data-bs-toggle="tab" data-bs-target="#accident-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-exclamation-octagon me-2"></i>@translator.Translate(userLanguage, "Repairs")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.UpgradeRecord)" id="upgrade-tab" data-bs-toggle="tab" data-bs-target="#upgrade-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-wrench-adjustable me-2"></i>Upgrades</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.UpgradeRecord)" id="upgrade-tab" data-bs-toggle="tab" data-bs-target="#upgrade-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-wrench-adjustable me-2"></i>@translator.Translate(userLanguage, "Upgrades")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.GasRecord)" id="gas-tab" data-bs-toggle="tab" data-bs-target="#gas-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-fuel-pump me-2"></i>Fuel</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.GasRecord)" id="gas-tab" data-bs-toggle="tab" data-bs-target="#gas-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-fuel-pump me-2"></i>@translator.Translate(userLanguage, "Fuel")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.SupplyRecord)" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop me-2"></i>Supplies</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.SupplyRecord)" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop me-2"></i>@translator.Translate(userLanguage, "Supplies")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.TaxRecord)" id="tax-tab" data-bs-toggle="tab" data-bs-target="#tax-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-currency-dollar me-2"></i>Taxes</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.TaxRecord)" id="tax-tab" data-bs-toggle="tab" data-bs-target="#tax-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-currency-dollar me-2"></i>@translator.Translate(userLanguage, "Taxes")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.NoteRecord)" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-journal-bookmark me-2"></i>Notes</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.NoteRecord)" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-journal-bookmark me-2"></i>@translator.Translate(userLanguage, "Notes")</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ReminderRecord)" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><div class="reminderBellDiv" style="display:inline-flex;"><i class="reminderBell bi bi-bell me-2"></i></div>Reminders</button>
<button class="nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ReminderRecord)" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><div class="reminderBellDiv" style="display:inline-flex;"><i class="reminderBell bi bi-bell me-2"></i></div>@translator.Translate(userLanguage, "Reminders")</button>
</li>
<li class="nav-item dropdown ms-auto" role="presentation">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">Manage Vehicle</a>
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">@translator.Translate(userLanguage, "Manage Vehicle")</a>
<ul class="dropdown-menu">
<li><button onclick="deleteVehicle(@Model.Id)" class="dropdown-item"><i class="bi bi-trash me-2"></i>Delete Vehicle</button></li>
<li><button onclick="deleteVehicle(@Model.Id)" class="dropdown-item"><i class="bi bi-trash me-2"></i>@translator.Translate(userLanguage, "Delete Vehicle")</button></li>
</ul>
</li>
</ul>

View File

@@ -1,6 +1,13 @@
@model ImportMode
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model ImportMode
<div class="modal-header">
<h5 class="modal-title">Import Data from CSV</h5>
<h5 class="modal-title">@translator.Translate(userLanguage, "Import Data from CSV")</h5>
<button type="button" class="btn-close" onclick="hideBulkImportModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -9,37 +16,40 @@
<div class="form-group">
<div class="col-12">
<div class="alert alert-warning" role="alert">
In order for this utility to function properly, your CSV file MUST be formatted exactly like the provided sample.
Dates must be supplied in a string.
Numbers must be supplied as numbers without currency formatting.
@translator.Translate(userLanguage, "In order for this utility to function properly, your CSV file MUST be formatted exactly like the provided sample. Dates must be supplied in a string. Numbers must be supplied as numbers without currency formatting.")
</div>
<div class="alert alert-danger" role="alert">
Failure to format the data correctly can cause data corruption. Please make sure you make a copy of the local database before proceeding.
@translator.Translate(userLanguage, "Failure to format the data correctly can cause data corruption. Please make sure you make a copy of the local database before proceeding.")
</div>
@if (Model == ImportMode.GasRecord)
{
<a class="btn btn-link" href="/defaults/gassample.csv" target="_blank">Download Sample</a>
} else if (Model == ImportMode.ServiceRecord || Model == ImportMode.RepairRecord || Model == ImportMode.UpgradeRecord)
<a class="btn btn-link" href="/defaults/gassample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
}
else if (Model == ImportMode.ServiceRecord || Model == ImportMode.RepairRecord || Model == ImportMode.UpgradeRecord)
{
<a class="btn btn-link" href="/defaults/servicerecordsample.csv" target="_blank">Download Sample</a>
} else if (Model == ImportMode.TaxRecord)
<a class="btn btn-link" href="/defaults/servicerecordsample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
}
else if (Model == ImportMode.TaxRecord)
{
<a class="btn btn-link" href="/defaults/taxrecordsample.csv" target="_blank">Download Sample</a>
} else if (Model == ImportMode.SupplyRecord)
<a class="btn btn-link" href="/defaults/taxrecordsample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
}
else if (Model == ImportMode.SupplyRecord)
{
<a class="btn btn-link" href="/defaults/supplysample.csv" target="_blank">Download Sample</a>
} else if (Model == ImportMode.PlanRecord)
<a class="btn btn-link" href="/defaults/supplysample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
}
else if (Model == ImportMode.PlanRecord)
{
<a class="btn btn-link" href="/defaults/plansample.csv" target="_blank">Download Sample</a>
} else if (Model == ImportMode.OdometerRecord)
<a class="btn btn-link" href="/defaults/plansample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
}
else if (Model == ImportMode.OdometerRecord)
{
<a class="btn btn-link" href="/defaults/odometersample.csv" target="_blank">Download Sample</a>
<a class="btn btn-link" href="/defaults/odometersample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
}
</div>
</div>
<div class="row mt-2">
<div class="col-12">
<label for="csvFileUploader">Upload CSV File</label>
<label for="csvFileUploader">@translator.Translate(userLanguage, "Upload CSV File")</label>
<input onChange="uploadFileAsync(this)" type="file" multiple accept=".csv" class="form-control-file" id="csvFileUploader">
</div>
</div>
@@ -47,8 +57,8 @@
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hideBulkImportModal()">Cancel</button>
<button type="button" onclick="importFromCsv()" class="btn btn-primary">Import</button>
<button type="button" class="btn btn-secondary" onclick="hideBulkImportModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" onclick="importFromCsv()" class="btn btn-primary">@translator.Translate(userLanguage, "Import")</button>
</div>
<script>
var uploadedFile = "";
@@ -73,13 +83,13 @@
getVehicleUpgradeRecords(vehicleId);
} else if (mode == "SupplyRecord") {
getVehicleSupplyRecords(vehicleId);
} else if (mode == "PlanRecord"){
} else if (mode == "PlanRecord") {
getVehiclePlanRecords(vehicleId);
} else if (mode == "OdometerRecord") {
getVehicleOdometerRecords(vehicleId);
}
} else {
errorToast("An error has occurred, please double check the data and try again.");
errorToast(genericErrorMessage());
}
});
}

View File

@@ -1,7 +1,14 @@
@model List<UserCollaborator>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model List<UserCollaborator>
<div class="row">
<div class="col-8">
<span class="lead">Collaborators</span>
<span class="lead">@translator.Translate(userLanguage, "Collaborators")</span>
</div>
<div class="col-4">
<button onclick="addCollaborator()" class="btn btn-link btn-sm"><i class="bi bi-person-add"></i></button>
@@ -11,8 +18,8 @@
<table class="table table-hover">
<thead>
<tr class="d-flex">
<th scope="col" class="col-8">Username</th>
<th scope="col" class="col-4">Delete</th>
<th scope="col" class="col-8">@translator.Translate(userLanguage, "Username")</th>
<th scope="col" class="col-4">@translator.Translate(userLanguage, "Delete")</th>
</tr>
</thead>
<tbody>
@@ -37,7 +44,7 @@
if (data) {
refreshCollaborators();
} else {
errorToast("An error occurred, please try again later");
errorToast(genericErrorMessage());
}
})
}

View File

@@ -1,9 +1,14 @@
@model CollisionRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model CollisionRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Repair Record" : "Edit Repair Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Repair Record") : translator.Translate(userLanguage,"Edit Repair Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddCollisionRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,22 +17,22 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="collisionRecordDate">Date</label>
<label for="collisionRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="collisionRecordDate" class="form-control" placeholder="Date repair was performed" value="@Model.Date">
<input type="text" id="collisionRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date repair was performed")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="collisionRecordMileage">Odometer</label>
<input type="number" id="collisionRecordMileage" class="form-control" placeholder="Odometer reading when repaired" value="@(isNew ? "" : Model.Mileage)">
<label for="collisionRecordDescription">Description</label>
<input type="text" id="collisionRecordDescription" class="form-control" placeholder="Description of item(s) repaired(i.e. Alternator)" value="@Model.Description">
<label for="collisionRecordCost">Cost</label>
<input type="text" id="collisionRecordCost" class="form-control" placeholder="Cost of the repair" value="@(isNew ? "" : Model.Cost)">
<label for="collisionRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="collisionRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when repaired")" value="@(isNew ? "" : Model.Mileage)">
<label for="collisionRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="collisionRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of item(s) repaired(i.e. Alternator)")" value="@Model.Description">
<label for="collisionRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="collisionRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of the repair")" value="@(isNew ? "" : Model.Cost)">
@if (isNew)
{
@await Html.PartialAsync("_SupplyStore", "RepairRecord")
}
<label for="collisionRecordTag">Tags(optional)</label>
<label for="collisionRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="collisionRecordTag">
@foreach (string tag in Model.Tags)
{
@@ -36,15 +41,15 @@
</select>
</div>
<div class="col-md-6 col-12">
<label for="collisionRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="collisionRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="collisionRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="collisionRecordFiles">Upload more documents</label>
<label for="collisionRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="collisionRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
@@ -54,13 +59,13 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck">
Add Reminder
@translator.Translate(userLanguage,"Add Reminder")
</label>
</div>
}
<label for="collisionRecordFiles">Upload documents(optional)</label>
<label for="collisionRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="collisionRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -71,25 +76,25 @@
@if (!isNew)
{
<div class="btn-group" style="margin-right:auto;">
<button type="button" class="btn btn-md mt-1 mb-1 btn-danger" onclick="deleteCollisionRecord(@Model.Id)">Delete</button>
<button type="button" class="btn btn-md mt-1 mb-1 btn-danger" onclick="deleteCollisionRecord(@Model.Id)">@translator.Translate(userLanguage,"Delete")</button>
<button type="button" class="btn btn-md btn-danger btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><h6 class="dropdown-header">Move To</h6></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'RepairRecord', 'ServiceRecord')">Service Records</a></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'RepairRecord', 'UpgradeRecord')">Upgrades</a></li>
<li><h6 class="dropdown-header">@translator.Translate(userLanguage,"Move To")</h6></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'RepairRecord', 'ServiceRecord')">@translator.Translate(userLanguage,"Service Records")</a></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'RepairRecord', 'UpgradeRecord')">@translator.Translate(userLanguage,"Upgrades")</a></li>
</ul>
</div>
}
<button type="button" class="btn btn-secondary" onclick="hideAddCollisionRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddCollisionRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveCollisionRecordToVehicle()">Add New Repair Record</button>
<button type="button" class="btn btn-primary" onclick="saveCollisionRecordToVehicle()">@translator.Translate(userLanguage,"Add New Repair Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveCollisionRecordToVehicle(true)">Edit Repair Record</button>
<button type="button" class="btn btn-primary" onclick="saveCollisionRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Repair Record")</button>
}
</div>
<script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userLanguage = userConfig.UserLanguage;
}
@model List<CollisionRecord>
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Repair Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary">@($"Total: {Model.Sum(x => x.Cost).ToString("C")}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Repair Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="filterTable('accident-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
@@ -26,21 +29,21 @@
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddCollisionRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Repair Record</button>
<button onclick="showAddCollisionRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Repair Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('RepairRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('RepairRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('RepairRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('RepairRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage,"Print")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddCollisionRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Repair Record</button>
<button onclick="showAddCollisionRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Repair Record")</button>
}
</div>
</div>
@@ -55,11 +58,11 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th>
<th scope="col" class="col-2">Odometer</th>
<th scope="col" class="col-3 col-xl-4">Description</th>
<th scope="col" class="col-2" onclick="toggleSort('accident-tab-pane', this)" style="cursor:pointer;">Cost</th>
<th scope="col" class="col-3">Notes</th>
<th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage,"Odometer")</th>
<th scope="col" class="col-3 col-xl-4">@translator.Translate(userLanguage,"Description")</th>
<th scope="col" class="col-2" onclick="toggleSort('accident-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage,"Cost")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage,"Notes")</th>
</tr>
</thead>
<tbody>
@@ -69,7 +72,7 @@
<td class="col-2 col-xl-1">@collisionRecord.Date.ToShortDateString()</td>
<td class="col-2">@collisionRecord.Mileage</td>
<td class="col-3 col-xl-4">@collisionRecord.Description</td>
<td class="col-2">@((hideZero && collisionRecord.Cost == default) ? "---" : collisionRecord.Cost.ToString("C"))</td>
<td class="col-2" data-record-type="cost">@((hideZero && collisionRecord.Cost == default) ? "---" : collisionRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(collisionRecord.Notes)</td>
</tr>
}

View File

@@ -1,4 +1,11 @@
@model CostMakeUpForVehicle
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model CostMakeUpForVehicle
@if (Model.CollisionRecordSum + Model.ServiceRecordSum + Model.GasRecordSum + Model.TaxRecordSum + Model.UpgradeRecordSum > 0)
{
<canvas id="pie-chart"></canvas>
@@ -9,10 +16,14 @@
new Chart($("#pie-chart"), {
type: 'pie',
data: {
labels: ["Service Records", "Repairs", "Upgrades", "Tax", "Fuel"],
labels: [decodeHTMLEntities('@translator.Translate(userLanguage, "Service Records")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Repairs")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Upgrades")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Tax")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Fuel")')],
datasets: [
{
label: "Expenses by Type",
label: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Type")'),
backgroundColor: ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"],
data: [
globalParseFloat('@Model.ServiceRecordSum'),
@@ -34,7 +45,7 @@
},
title: {
display: true,
text: "Expenses by Type",
text: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Type")'),
color: useDarkMode ? "#fff" : "#000"
},
}
@@ -46,6 +57,6 @@
else
{
<div class="text-center">
<h4>No data found or all records have zero sums, insert records with non-zero sums to see visualizations here.</h4>
<h4>@translator.Translate(userLanguage, "No data found or all records have zero sums, insert records with non-zero sums to see visualizations here.")</h4>
</div>
}

View File

@@ -1,6 +1,7 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject IGasHelper gasHelper
@inject ITranslationHelper translator
@model GasRecordViewModelContainer
@{
var userConfig = config.GetUserConfig(User);
@@ -10,8 +11,10 @@
var hideZero = userConfig.HideZero;
var useThreeDecimals = userConfig.UseThreeDecimalGasCost;
var gasCostFormat = useThreeDecimals ? "C3" : "C2";
var userLanguage = userConfig.UserLanguage;
var useKwh = Model.UseKwh;
var useHours = Model.UseHours;
var recordTags = Model.GasRecords.SelectMany(x => x.Tags).Distinct();
string preferredFuelEconomyUnit = userConfig.PreferredGasMileageUnit;
string preferredGasUnit = userConfig.PreferredGasUnit;
string consumptionUnit;
@@ -41,39 +44,49 @@
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Gas Records: {Model.GasRecords.Count()}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Gas Records")}: {Model.GasRecords.Count()}")</span>
@if (Model.GasRecords.Where(x => x.MilesPerGallon > 0).Any())
{
<span class="ms-2 badge bg-primary" id="averageFuelMileageLabel">@($"Average Fuel Economy: {gasHelper.GetAverageGasMileage(Model.GasRecords, useMPG)}")</span>
<span class="ms-2 badge bg-primary" id="averageFuelMileageLabel">@($"{translator.Translate(userLanguage,"Average Fuel Economy")}: {gasHelper.GetAverageGasMileage(Model.GasRecords, useMPG)}")</span>
if (useMPG)
{
<span class="ms-2 badge bg-primary" id="minFuelMileageLabel">@($"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" id="maxFuelMileageLabel">@($"Max Fuel Economy: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
<span class="ms-2 badge bg-primary" id="minFuelMileageLabel">@($"{translator.Translate(userLanguage,"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" id="maxFuelMileageLabel">@($"{translator.Translate(userLanguage,"Max Fuel Economy")}: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
} else
{
<span class="ms-2 badge bg-primary" id="minFuelMileageLabel">@($"Min Fuel Economy: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
<span class="ms-2 badge bg-primary" id="maxFuelMileageLabel">@($"Max Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
<span class="ms-2 badge bg-primary" id="minFuelMileageLabel">@($"{translator.Translate(userLanguage,"Min Fuel Economy")}: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
<span class="ms-2 badge bg-primary" id="maxFuelMileageLabel">@($"{translator.Translate(userLanguage,"Max Fuel Economy")}: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
}
}
<span class="ms-2 badge bg-success">@($"Total Fuel Consumed: {Model.GasRecords.Sum(x => x.Gallons).ToString("F")}")</span>
<span class="ms-2 badge bg-success">@($"Total Cost: {Model.GasRecords.Sum(x => x.Cost).ToString(gasCostFormat)}")</span>
<span class="ms-2 badge bg-success">@($"{translator.Translate(userLanguage,"Total Fuel Consumed")}: {Model.GasRecords.Sum(x => x.Gallons).ToString("F")}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total Cost")}: {Model.GasRecords.Sum(x => x.Cost).ToString(gasCostFormat)}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="toggleGasFilter(this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
}
<datalist id="tagList">
@foreach (string recordTag in recordTags)
{
<!option value="@recordTag"></!option>
}
</datalist>
</div>
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddGasRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Gas Record</button>
<button onclick="showAddGasRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Gas Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('GasRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('GasRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('GasRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('GasRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage,"Print")</a></li>
</ul>
</div>
} else {
<button onclick="showAddGasRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Gas Record</button>
<button onclick="showAddGasRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Gas Record")</button>
}
</div>
</div>
@@ -87,23 +100,23 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2">Date Refueled</th>
<th scope="col" class="col-2">Odometer(@(distanceUnit))</th>
<th scope="col" class="col-2" data-gas="consumption" data-unit="@consumptionUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">Consumption(@(consumptionUnit))</th>
<th scope="col" class="col-4" data-gas="fueleconomy" data-unit="@fuelEconomyUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">Fuel Economy(@(fuelEconomyUnit))</th>
<th scope="col" class="col-1" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">Cost</th>
<th scope="col" class="col-1" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">Unit Cost</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage,"Date Refueled")</th>
<th scope="col" class="col-2">@($"{translator.Translate(userLanguage,"Odometer")}({distanceUnit})")</th>
<th scope="col" class="col-2" 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-4" 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" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage,"Cost")</th>
<th scope="col" class="col-1" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage,"Unit Cost")</th>
</tr>
</thead>
<tbody>
@foreach (GasRecordViewModel gasRecord in Model.GasRecords)
{
<tr class="d-flex" style="cursor:pointer;" onclick="showEditGasRecordModal(@gasRecord.Id)">
<tr class="d-flex" style="cursor:pointer;" onclick="showEditGasRecordModal(@gasRecord.Id)" data-tags='@string.Join(" ", gasRecord.Tags)'>
<td class="col-2">@gasRecord.Date</td>
<td class="col-2">@gasRecord.Mileage</td>
<td class="col-2" data-gas-type="consumption">@gasRecord.Gallons.ToString("F")</td>
<td class="col-4" data-gas-type="fueleconomy">@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-1">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td>
<td class="col-2" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage">@gasRecord.Mileage</td>
<td class="col-2" data-gas-type="consumption" data-gas-aggregate="@gasRecord.Gallons">@gasRecord.Gallons.ToString("F")</td>
<td class="col-4" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-1" data-record-type="cost">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td>
<td class="col-1" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td>
</tr>
}
@@ -130,4 +143,5 @@
{
@:convertGasConsumptionUnits(decodeHTMLEntities('@consumptionUnit'), decodeHTMLEntities('@preferredGasUnit'), false);
}
</script>

View File

@@ -1,5 +1,10 @@
@model List<CostForVehicleByMonth>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<CostForVehicleByMonth>
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
var sortedByMPG = Model.OrderBy(x => x.Cost).ToList();
}
@@ -27,7 +32,7 @@
labels: barGraphLabels,
datasets: [
{
label: "Expenses by Month",
label: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Month")'),
backgroundColor: barGraphColors,
data: barGraphData
}
@@ -38,7 +43,7 @@
title: {
display: true,
color: useDarkMode ? "#fff" : "#000",
text: 'Expenses by Month'
text: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Month")')
},
legend: {
display: false,
@@ -67,6 +72,6 @@
} else
{
<div class="text-center">
<h4>No data found, insert/select some data to see visualizations here.</h4>
<h4>@translator.Translate(userLanguage,"No data found, insert/select some data to see visualizations here.")</h4>
</div>
}

View File

@@ -1,9 +1,12 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model GasRecordInputContainer
@{
var useMPG = config.GetUserConfig(User).UseMPG;
var useUKMPG = config.GetUserConfig(User).UseUKMPG;
var userConfig = config.GetUserConfig(User);
var useMPG = userConfig.UseMPG;
var useUKMPG = userConfig.UseUKMPG;
var userLanguage = userConfig.UserLanguage;
var useKwh = Model.UseKwh;
var useHours = Model.UseHours;
var isNew = Model.GasRecord.Id == 0;
@@ -34,7 +37,7 @@
}
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Gas Record" : "Edit Gas Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Gas Record") : translator.Translate(userLanguage,"Edit Gas Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddGasRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -43,57 +46,64 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="gasRecordDate">Date</label>
<label for="gasRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="gasRecordDate" placeholder="Date refueled" class="form-control" value="@Model.GasRecord.Date">
<input type="text" id="gasRecordDate" placeholder="@translator.Translate(userLanguage,"Date refueled")" class="form-control" value="@Model.GasRecord.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="gasRecordMileage">Odometer Reading(@distanceUnit)</label>
<input type="number" id="gasRecordMileage" class="form-control" placeholder="Odometer reading when refueled" value="@(isNew ? "" : Model.GasRecord.Mileage)">
<label for="gasRecordGallons">Fuel Consumption(@(consumptionUnit))</label>
<input type="text" id="gasRecordGallons" class="form-control" placeholder="Amount of gas refueled" value="@(isNew ? "" : Model.GasRecord.Gallons)">
<label for="gasRecordMileage">@($"{translator.Translate(userLanguage,"Odometer Reading")}({distanceUnit})")</label>
<input type="number" id="gasRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when refueled")" value="@(isNew ? "" : Model.GasRecord.Mileage)">
<label for="gasRecordGallons">@($"{translator.Translate(userLanguage, "Fuel Consumption")}({consumptionUnit})")</label>
<input type="text" id="gasRecordGallons" class="form-control" placeholder="@translator.Translate(userLanguage,"Amount of gas refueled")" value="@(isNew ? "" : Model.GasRecord.Gallons)">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="gasIsFillToFull" checked="@Model.GasRecord.IsFillToFull">
<label class="form-check-label" for="gasIsFillToFull">Is Filled To Full</label>
<label class="form-check-label" for="gasIsFillToFull">@translator.Translate(userLanguage,"Is Filled To Full")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="gasIsMissed" checked="@Model.GasRecord.MissedFuelUp">
<label class="form-check-label" for="gasIsMissed">Missed Fuel Up(Skip MPG Calculation)</label>
<label class="form-check-label" for="gasIsMissed">@translator.Translate(userLanguage,"Missed Fuel Up(Skip MPG Calculation)")</label>
</div>
<label for="GasRecordCost">Cost</label>
<label for="GasRecordCost">@translator.Translate(userLanguage,"Cost")</label>
@if (isNew)
{
<div class="input-group">
<input type="text" id="gasRecordCost" class="form-control" placeholder="Cost of gas refueled" value="@(isNew ? "" : Model.GasRecord.Cost)">
<input type="text" id="gasRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of gas refueled")" value="@(isNew ? "" : Model.GasRecord.Cost)">
<div class="input-group-text">
<select class="form-select form-select-sm" id="gasCostType">
<option value="total">Total</option>
<option value="unit">Unit</option>
<option value="total">@translator.Translate(userLanguage,"Total")</option>
<option value="unit">@translator.Translate(userLanguage,"Unit")</option>
</select>
</div>
</div>
} else
{
<input type="text" id="gasRecordCost" class="form-control" placeholder="Cost of gas refueled" value="@(isNew ? "" : Model.GasRecord.Cost)">
<input type="text" id="gasRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of gas refueled")" value="@(isNew ? "" : Model.GasRecord.Cost)">
}
<label for="gasRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="gasRecordTag">
@foreach (string tag in Model.GasRecord.Tags)
{
<!option value="@tag">@tag</!option>
}
</select>
</div>
<div class="col-md-6 col-12">
<label for="gasRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="gasRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="gasRecordNotes" class="form-control" rows="5">@Model.GasRecord.Notes</textarea>
@if (Model.GasRecord.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.GasRecord.Files)
<label for="gasRecordFiles">Upload more documents</label>
<label for="gasRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="gasRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
{
<label for="gasRecordFiles">Upload documents(optional)</label>
<label for="gasRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="gasRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -103,16 +113,16 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteGasRecord(@Model.GasRecord.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deleteGasRecord(@Model.GasRecord.Id)" style="margin-right:auto;">@translator.Translate(userLanguage,"Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddGasRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddGasRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveGasRecordToVehicle()">Add New Gas Record</button>
<button type="button" class="btn btn-primary" onclick="saveGasRecordToVehicle()">@translator.Translate(userLanguage,"Add New Gas Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveGasRecordToVehicle(true)">Edit Gas Record</button>
<button type="button" class="btn btn-primary" onclick="saveGasRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Gas Record")</button>
}
</div>
<script>

View File

@@ -1,9 +1,14 @@
@model List<CostForVehicleByMonth>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<CostForVehicleByMonth>
@{
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 userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@if (Model.Where(x=>x.Cost > 0).Any())
@if (Model.Where(x => x.Cost > 0).Any())
{
<canvas id="bar-chart-mpg"></canvas>
@@ -28,7 +33,7 @@
labels: barGraphLabels,
datasets: [
{
label: "Fuel Mileage by Month",
label: decodeHTMLEntities('@translator.Translate(userLanguage, "Fuel Mileage by Month")'),
backgroundColor: barGraphColors,
data: barGraphData
}
@@ -39,7 +44,7 @@
title: {
display: true,
color: useDarkMode ? "#fff" : "#000",
text: 'Fuel Mileage by Month'
text: decodeHTMLEntities('@translator.Translate(userLanguage, "Fuel Mileage by Month")')
},
legend: {
display: false,
@@ -65,9 +70,10 @@
});
}
</script>
} else
}
else
{
<div class="text-center">
<h4>No data found, insert/select some data to see visualizations here.</h4>
<h4>@translator.Translate(userLanguage,"No data found, insert/select some data to see visualizations here.")</h4>
</div>
}

View File

@@ -1,9 +1,14 @@
@model Note
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model Note
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Note" : "Edit Note")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage, "Add New Note") : translator.Translate(userLanguage, "Edit Note"))</h5>
<button type="button" class="btn-close" onclick="hideAddNoteModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -14,17 +19,35 @@
<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>
<label class="form-check-label" for="noteIsPinned">@translator.Translate(userLanguage,"Pinned")</label>
</div>
<label for="noteDescription">Description</label>
<input type="text" id="noteDescription" class="form-control" placeholder="Description of the note" value="@(isNew ? "" : Model.Description)">
<label for="noteDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="noteDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of the note")" value="@(isNew ? "" : Model.Description)">
</div>
<div class="col-12">
<label for="noteTextArea">Notes<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="noteTextArea">@translator.Translate(userLanguage,"Notes")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea class="form-control vehicleNoteContainer" id="noteTextArea">@Model.NoteText</textarea>
</div>
<div class="col-12">
<label for="noteRecordTag">Tags(optional)</label>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="serviceRecordFiles">@translator.Translate(userLanguage, "Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="noteFiles">
<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
</div>
}
else
{
<label for="serviceRecordFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="noteFiles">
<br />
<small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
}
</div>
<div class="col-12">
<label for="noteRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="noteRecordTag">
@foreach (string tag in Model.Tags)
{
@@ -39,19 +62,27 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteNote(@Model.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deleteNote(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage,"Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddNoteModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddNoteModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveNoteToVehicle()">Add New Note</button>
<button type="button" class="btn btn-primary" onclick="saveNoteToVehicle()">@translator.Translate(userLanguage,"Add New Note")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveNoteToVehicle(true)">Edit Note</button>
<button type="button" class="btn btn-primary" onclick="saveNoteToVehicle(true)">@translator.Translate(userLanguage,"Edit Note")</button>
}
</div>
<script>
var uploadedFiles = [];
getUploadedFilesFromModel();
function getUploadedFilesFromModel() {
@foreach (UploadedFiles filesUploaded in Model.Files)
{
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
}
}
function getNoteModelData(){
return { id: @Model.Id}
}

View File

@@ -1,11 +1,16 @@
@model List<Note>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<Note>
@{
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Notes: {Model.Count()}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Notes")}: {Model.Count()}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="filterTable('notes-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
@@ -18,7 +23,7 @@
</datalist>
</div>
<div>
<button onclick="showAddNoteModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Note</button>
<button onclick="showAddNoteModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Note")</button>
</div>
</div>
</div>
@@ -32,8 +37,8 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-3">Description</th>
<th scope="col" class="col-9">Note</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage,"Description")</th>
<th scope="col" class="col-9">@translator.Translate(userLanguage,"Note")</th>
</tr>
</thead>
<tbody>
@@ -47,7 +52,7 @@
{
<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" data-record-type="cost">@CarCareTracker.Helper.StaticHelper.TruncateStrings(note.NoteText, 100)</td>
</tr>
}
</tbody>

View File

@@ -1,9 +1,14 @@
@model OdometerRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model OdometerRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Odometer Record" : "Edit Odometer Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Odometer Record") : translator.Translate(userLanguage,"Edit Odometer Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddOdometerRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,14 +17,14 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="odometerRecordDate">Date</label>
<label for="odometerRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="odometerRecordDate" class="form-control" placeholder="Date recorded" value="@Model.Date">
<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="odometerRecordMileage">Odometer</label>
<input type="number" id="odometerRecordMileage" class="form-control" placeholder="Odometer reading" value="@(isNew ? "" : Model.Mileage)">
<label for="odometerRecordTag">Tags(optional)</label>
<label for="odometerRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="odometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading")" value="@(isNew ? "" : Model.Mileage)">
<label for="odometerRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="odometerRecordTag">
@foreach (string tag in Model.Tags)
{
@@ -28,22 +33,22 @@
</select>
</div>
<div class="col-md-6 col-12">
<label for="odometerRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="odometerRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<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">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="odometerRecordFiles">Upload more documents</label>
<label for="odometerRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="odometerRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
{
<label for="odometerRecordFiles">Upload documents(optional)</label>
<label for="odometerRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="odometerRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -53,16 +58,16 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteOdometerRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deleteOdometerRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage,"Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddOdometerRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddOdometerRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveOdometerRecordToVehicle()">Add New Odometer Record</button>
<button type="button" class="btn btn-primary" onclick="saveOdometerRecordToVehicle()">@translator.Translate(userLanguage,"Add New Odometer Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveOdometerRecordToVehicle(true)">Edit Odometer Record</button>
<button type="button" class="btn btn-primary" onclick="saveOdometerRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Odometer Record")</button>
}
</div>
<script>

View File

@@ -1,15 +1,18 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userLanguage = userConfig.UserLanguage;
}
@model List<OdometerRecord>
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Odometer Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Odometer Records")}: {Model.Count()}")</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>
@@ -25,21 +28,21 @@
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddOdometerRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Odometer Record</button>
<button onclick="showAddOdometerRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Odometer Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('OdometerRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('OdometerRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('OdometerRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('OdometerRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage,"Print")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddOdometerRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Odometer Record</button>
<button onclick="showAddOdometerRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Odometer Record")</button>
}
</div>
</div>
@@ -54,9 +57,9 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th>
<th scope="col" class="col-3">Odometer</th>
<th scope="col" class="col-7 col-xl-8">Notes</th>
<th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage,"Odometer")</th>
<th scope="col" class="col-7 col-xl-8">@translator.Translate(userLanguage,"Notes")</th>
</tr>
</thead>
<tbody>
@@ -64,7 +67,7 @@
{
<tr class="d-flex" style="cursor:pointer;" onclick="showEditOdometerRecordModal(@odometerRecord.Id)" data-tags='@string.Join(" ", odometerRecord.Tags)'>
<td class="col-2 col-xl-1">@odometerRecord.Date.ToShortDateString()</td>
<td class="col-3">@odometerRecord.Mileage</td>
<td class="col-3" data-record-type="cost">@odometerRecord.Mileage</td>
<td class="col-7 col-xl-8 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td>
</tr>
}

View File

@@ -1,9 +1,14 @@
@model PlanRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model PlanRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Plan Record" : "Edit Plan Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage, "Add New Plan Record") : translator.Translate(userLanguage, "Edit Plan Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddPlanRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,55 +17,57 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="planRecordDescription">Description</label>
<input type="text" id="planRecordDescription" class="form-control" placeholder="Describe the Plan" value="@Model.Description">
<label for="planRecordCost">Cost</label>
<input type="text" id="planRecordCost" class="form-control" placeholder="Cost of the Plan" value="@Model.Cost">
<label for="planRecordDescription">@translator.Translate(userLanguage, "Description")</label>
<input type="text" id="planRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage, "Describe the Plan")" value="@Model.Description">
<label for="planRecordCost">@translator.Translate(userLanguage, "Cost")</label>
<input type="text" id="planRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage, "Cost of the Plan")" value="@Model.Cost">
@if (isNew)
{
@await Html.PartialAsync("_SupplyStore", "PlanRecord")
}
<label for="planRecordType">Type</label>
<label for="planRecordType">@translator.Translate(userLanguage, "Type")</label>
<select class="form-select" id="planRecordType">
<!option value="ServiceRecord" @(Model.ImportMode == ImportMode.ServiceRecord || isNew ? "selected" : "")>Service</!option>
<!option value="RepairRecord" @(Model.ImportMode == ImportMode.RepairRecord ? "selected" : "")>Repair</!option>
<!option value="UpgradeRecord" @(Model.ImportMode == ImportMode.UpgradeRecord ? "selected" : "")>Upgrade</!option>
<!option value="ServiceRecord" @(Model.ImportMode == ImportMode.ServiceRecord || isNew ? "selected" : "")>@translator.Translate(userLanguage, "Service")</!option>
<!option value="RepairRecord" @(Model.ImportMode == ImportMode.RepairRecord ? "selected" : "")>@translator.Translate(userLanguage, "Repair")</!option>
<!option value="UpgradeRecord" @(Model.ImportMode == ImportMode.UpgradeRecord ? "selected" : "")>@translator.Translate(userLanguage, "Upgrade")</!option>
</select>
<label for="planRecordPriority">Priority</label>
<label for="planRecordPriority">@translator.Translate(userLanguage, "Priority")</label>
<select class="form-select" id="planRecordPriority">
<!option value="Critical" @(Model.Priority == PlanPriority.Critical ? "selected" : "")>Critical</!option>
<!option value="Normal" @(Model.Priority == PlanPriority.Normal || isNew ? "selected" : "")>Normal</!option>
<!option value="Low" @(Model.Priority == PlanPriority.Low ? "selected" : "")>Low</!option>
<!option value="Critical" @(Model.Priority == PlanPriority.Critical ? "selected" : "")>@translator.Translate(userLanguage, "Critical")</!option>
<!option value="Normal" @(Model.Priority == PlanPriority.Normal || isNew ? "selected" : "")>@translator.Translate(userLanguage, "Normal")</!option>
<!option value="Low" @(Model.Priority == PlanPriority.Low ? "selected" : "")>@translator.Translate(userLanguage, "Low")</!option>
</select>
<label for="planRecordProgress">Current Stage</label>
<label for="planRecordProgress">@translator.Translate(userLanguage, "Current Stage")</label>
<select class="form-select" id="planRecordProgress">
<!option value="Backlog" @(Model.Progress == PlanProgress.Backlog ||isNew ? "selected" : "")>Planned</!option>
<!option value="InProgress" @(Model.Progress == PlanProgress.InProgress ? "selected" : "")>Doing</!option>
<!option value="Testing" @(Model.Progress == PlanProgress.Testing ? "selected" : "")>Testing</!option>
<!option value = "Backlog" @(Model.Progress == PlanProgress.Backlog || isNew ? "selected" : "")>@translator.Translate(userLanguage, "Planned")</!option>
<!option value="InProgress" @(Model.Progress == PlanProgress.InProgress ? "selected" : "")>@translator.Translate(userLanguage, "Doing")</!option>
<!option value = "Testing" @(Model.Progress == PlanProgress.Testing ? "selected" : "")>@translator.Translate(userLanguage, "Testing")</!option>
</select>
@if (!isNew)
{
<label>Date Created: @Model.DateCreated</label>
<label>Last Modified: @Model.DateModified</label>
<label>@($"{translator.Translate(userLanguage, "Date Created")}: {Model.DateCreated}")</label>
<label>@($"{translator.Translate(userLanguage, "Last Modified")}: {Model.DateModified}")</label>
}
</div>
<div class="col-md-6 col-12">
<label for="planRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="planRecordNotes">@translator.Translate(userLanguage, "Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="planRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="planRecordFiles">Upload more documents</label>
<label for="planRecordFiles">@translator.Translate(userLanguage, "Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="planRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
</div>
}
else
{
<label for="planRecordFiles">Upload documents(optional)</label>
<label for="planRecordFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="planRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br />
<small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -70,16 +77,16 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deletePlanRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deletePlanRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage, "Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddPlanRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddPlanRecordModal()">@translator.Translate(userLanguage, "Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="savePlanRecordToVehicle()">Add New Plan Record</button>
<button type="button" class="btn btn-primary" onclick="savePlanRecordToVehicle()">@translator.Translate(userLanguage, "Add New Plan Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="savePlanRecordToVehicle(true)">Edit Plan Record</button>
<button type="button" class="btn btn-primary" onclick="savePlanRecordToVehicle(true)">@translator.Translate(userLanguage, "Edit Plan Record")</button>
}
</div>
<script>

View File

@@ -1,8 +1,11 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var userLanguage = userConfig.UserLanguage;
var backLogItems = Model.Where(x => x.Progress == PlanProgress.Backlog).OrderBy(x=>x.Priority);
var inProgressItems = Model.Where(x => x.Progress == PlanProgress.InProgress).OrderBy(x => x.Priority);
var testingItems = Model.Where(x => x.Progress == PlanProgress.Testing).OrderBy(x => x.Priority);
@@ -12,25 +15,25 @@
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Plan Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-success">@($"{translator.Translate(userLanguage,"# of Plan Records")}: {Model.Count()}")</span>
</div>
<div>
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddPlanRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Plan Record</button>
<button onclick="showAddPlanRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Plan Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('PlanRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('PlanRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('PlanRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('PlanRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddPlanRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Plan Record</button>
<button onclick="showAddPlanRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Plan Record")</button>
}
</div>
</div>
@@ -46,7 +49,7 @@
<div class="col-3 d-flex flex-column swimlane mid" ondragover="dragOver(event)" ondrop="dropBox(event, 'Backlog')">
<div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
<span class="display-7">Planned</span>
<span class="display-7">@translator.Translate(userLanguage,"Planned")</span>
</div>
</div>
@foreach (PlanRecord planRecord in backLogItems)
@@ -57,7 +60,7 @@
<div class="col-3 d-flex flex-column swimlane mid" ondragover="dragOver(event)" ondrop="dropBox(event, 'InProgress')">
<div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
<span class="display-7">Doing</span>
<span class="display-7">@translator.Translate(userLanguage,"Doing")</span>
</div>
</div>
@foreach (PlanRecord planRecord in inProgressItems)
@@ -68,7 +71,7 @@
<div class="col-3 d-flex flex-column swimlane" ondragover="dragOver(event)" ondrop="dropBox(event, 'Testing')">
<div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
<span class="display-7">Testing</span>
<span class="display-7">@translator.Translate(userLanguage,"Testing")</span>
</div>
</div>
@foreach (PlanRecord planRecord in testingItems)
@@ -79,7 +82,7 @@
<div class="col-3 d-flex flex-column swimlane end" ondragover="dragOver(event)" ondrop="dropBox(event, 'Done')">
<div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
<span class="display-7">Done</span>
<span class="display-7">@translator.Translate(userLanguage,"Done")</span>
</div>
</div>
@foreach (PlanRecord planRecord in doneItems)

View File

@@ -1,4 +1,11 @@
@model ReminderMakeUpForVehicle
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model ReminderMakeUpForVehicle
@if (Model.UrgentCount + Model.VeryUrgentCount + Model.NotUrgentCount + Model.PastDueCount > 0)
{
<canvas id="donut-chart"></canvas>
@@ -9,10 +16,15 @@
new Chart($("#donut-chart"), {
type: 'doughnut',
data: {
labels: ["Not Urgent", "Urgent", "Very Urgent", "Past Due"],
labels: [
decodeHTMLEntities('@translator.Translate(userLanguage, "Not Urgent")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Urgent")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Very Urgent")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Past Due")')
],
datasets: [
{
label: "Reminders by Category",
label: decodeHTMLEntities('@translator.Translate(userLanguage, "Reminders by Category")'),
backgroundColor: ["#488f31", "#ffa600", "#de425b", "#cccccc"],
data: [
@Model.NotUrgentCount,
@@ -33,7 +45,7 @@
},
title: {
display: true,
text: "Reminders by Urgency",
text: decodeHTMLEntities('@translator.Translate(userLanguage, "Reminders by Urgency")'),
color: useDarkMode ? "#fff" : "#000"
},
}
@@ -45,6 +57,6 @@
else
{
<div class="text-center">
<h4>No data found, create reminders to see visualizations here.</h4>
<h4>@translator.Translate(userLanguage,"No data found, create reminders to see visualizations here.")</h4>
</div>
}

View File

@@ -1,9 +1,14 @@
@model ReminderRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model ReminderRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Reminder" : "Edit Reminder")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage, "Add New Reminder") : translator.Translate(userLanguage, "Edit Reminder"))</h5>
<button type="button" class="btn-close" onclick="hideAddReminderRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,42 +17,42 @@
<div class="row">
<div class="col-md-6 col-12" id="reminderOptions">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="reminderDescription">Description</label>
<input type="text" id="reminderDescription" class="form-control" placeholder="Reminder Description" value="@Model.Description">
<label>Remind me on:</label>
<label for="reminderDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="reminderDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Reminder Description")" value="@Model.Description">
<label>@translator.Translate(userLanguage,"Remind me on")</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricDate" value="@(ReminderMetric.Date)" checked="@(Model.Metric == ReminderMetric.Date)">
<label class="form-check-label" for="reminderMetricDate">Date</label>
<label class="form-check-label" for="reminderMetricDate">@translator.Translate(userLanguage,"Date")</label>
</div>
<div class="input-group">
<input type="text" id="reminderDate" class="form-control" placeholder="Future Date" value="@Model.Date">
<input type="text" id="reminderDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Future Date")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricOdometer" value="@(ReminderMetric.Odometer)" checked="@(Model.Metric == ReminderMetric.Odometer)">
<label class="form-check-label" for="reminderMetricOdometer">Odometer</label>
<label class="form-check-label" for="reminderMetricOdometer">@translator.Translate(userLanguage,"Odometer")</label>
</div>
<div class="input-group">
<input type="number" id="reminderMileage" class="form-control" placeholder="Future Odometer Reading" value="@(isNew ? "" : Model.Mileage)">
<input type="number" id="reminderMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Future Odometer Reading")" value="@(isNew ? "" : Model.Mileage)">
<div class="input-group-text">
<button type="button" class="btn btn-sm btn-primary" onclick="appendMileageToOdometer(500)">+500</button>
</div>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricBoth" value="@(ReminderMetric.Both)" checked="@(Model.Metric == ReminderMetric.Both)">
<label class="form-check-label" for="reminderMetricBoth">Whichever comes first</label>
<label class="form-check-label" for="reminderMetricBoth">@translator.Translate(userLanguage,"Whichever comes first")</label>
</div>
</div>
<div class="col-md-6 col-12">
<label for="reminderNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="reminderNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="reminderNotes" class="form-control" rows="5">@Model.Notes</textarea>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" onChange="enableRecurring()" role="switch" id="reminderIsRecurring" checked="@Model.IsRecurring">
<label class="form-check-label" for="reminderIsRecurring">Is Recurring</label>
<label class="form-check-label" for="reminderIsRecurring">@translator.Translate(userLanguage,"Is Recurring")</label>
</div>
<label for="reminderRecurringMileage">Odometer</label>
<label for="reminderRecurringMileage">@translator.Translate(userLanguage,"Odometer")</label>
<select class="form-select" onchange="checkCustomMileageInterval()" id="reminderRecurringMileage" @(Model.IsRecurring ? "" : "disabled")>
<!option value="Other" @(Model.ReminderMileageInterval == ReminderMileageInterval.Other ? "selected" : "")>@(Model.ReminderMileageInterval == ReminderMileageInterval.Other && Model.CustomMileageInterval > 0 ? $"Other: {Model.CustomMileageInterval}" : "Other")</!option>
<!option value="Other" @(Model.ReminderMileageInterval == ReminderMileageInterval.Other ? "selected" : "")>@(Model.ReminderMileageInterval == ReminderMileageInterval.Other && Model.CustomMileageInterval > 0 ? $"{translator.Translate(userLanguage, "Other")}: {Model.CustomMileageInterval}" : $"{translator.Translate(userLanguage, "Other")}") </!option>
<!option value="FiftyMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiftyMiles ? "selected" : "")>50 mi. / Km</!option>
<!option value="OneHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredMiles ? "selected" : "")>100 mi. / Km</!option>
<!option value="FiveHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiveHundredMiles ? "selected" : "")>500 mi. / Km</!option>
@@ -68,12 +73,12 @@
</select>
<label for="reminderRecurringMonth">Month</label>
<select class="form-select" id="reminderRecurringMonth" @(Model.IsRecurring ? "" : "disabled")>
<!option value="ThreeMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>3 Months</!option>
<!option value="SixMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>6 Months</!option>
<!option value="OneYear" @(Model.ReminderMonthInterval == ReminderMonthInterval.OneYear ? "selected" : "")>1 Year</!option>
<!option value="TwoYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>2 Years</!option>
<!option value="ThreeYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>3 Years</!option>
<!option value="FiveYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>5 Years</!option>
<!option value="ThreeMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>@translator.Translate(userLanguage,"3 Months")</!option>
<!option value="SixMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>@translator.Translate(userLanguage,"6 Months")</!option>
<!option value="OneYear" @(Model.ReminderMonthInterval == ReminderMonthInterval.OneYear ? "selected" : "")>@translator.Translate(userLanguage, "1 Year")</!option>
<!option value="TwoYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>@translator.Translate(userLanguage, "2 Years")</!option>
<!option value="ThreeYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>@translator.Translate(userLanguage, "3 Years")</!option>
<!option value="FiveYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>@translator.Translate(userLanguage, "5 Years")</!option>
</select>
</div>
</div>
@@ -83,16 +88,16 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteReminderRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deleteReminderRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage, "Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddReminderRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddReminderRecordModal()">@translator.Translate(userLanguage, "Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveReminderRecordToVehicle()">Add New Reminder</button>
<button type="button" class="btn btn-primary" onclick="saveReminderRecordToVehicle()">@translator.Translate(userLanguage, "Add New Reminder")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveReminderRecordToVehicle(true)">Edit Reminder</button>
<button type="button" class="btn btn-primary" onclick="saveReminderRecordToVehicle(true)">@translator.Translate(userLanguage, "Edit Reminder")</button>
}
</div>
<script>

View File

@@ -1,18 +1,23 @@
@model List<ReminderRecordViewModel>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<ReminderRecordViewModel>
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var hasRefresh = Model.Where(x => (x.Urgency == ReminderUrgency.VeryUrgent || x.Urgency == ReminderUrgency.PastDue) && x.IsRecurring).Any();
}
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Reminders: {Model.Count()}")</span>
<span class="ms-2 badge bg-secondary">@($"Past Due: {Model.Where(x => x.Urgency == ReminderUrgency.PastDue).Count()}")</span>
<span class="ms-2 badge bg-danger">@($"Very Urgent: {Model.Where(x=>x.Urgency == ReminderUrgency.VeryUrgent).Count()}")</span>
<span class="ms-2 badge bg-warning">@($"Urgent: {Model.Where(x => x.Urgency == ReminderUrgency.Urgent).Count()}")</span>
<span class="ms-2 badge bg-success">@($"Not Urgent: {Model.Where(x => x.Urgency == ReminderUrgency.NotUrgent).Count()}")</span>
<span class="ms-2 badge bg-success">@($"{translator.Translate(userLanguage, "# of Reminders")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-secondary">@($"{translator.Translate(userLanguage, "Past Due")}: {Model.Where(x => x.Urgency == ReminderUrgency.PastDue).Count()}")</span>
<span class="ms-2 badge bg-danger">@($"{translator.Translate(userLanguage, "Very Urgent")}: {Model.Where(x=>x.Urgency == ReminderUrgency.VeryUrgent).Count()}")</span>
<span class="ms-2 badge bg-warning">@($"{translator.Translate(userLanguage, "Urgent")}: {Model.Where(x => x.Urgency == ReminderUrgency.Urgent).Count()}")</span>
<span class="ms-2 badge bg-success">@($"{translator.Translate(userLanguage, "Not Urgent")}: {Model.Where(x => x.Urgency == ReminderUrgency.NotUrgent).Count()}")</span>
</div>
<div>
<button onclick="showAddReminderModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Reminder</button>
<button onclick="showAddReminderModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Reminder")</button>
</div>
</div>
</div>
@@ -26,15 +31,15 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-1">Urgency</th>
<th scope="col" class="col-2">Metric</th>
<th scope="col" class="@(hasRefresh ? "col-4" : "col-5")">Description</th>
<th scope="col" class="col-3">Notes</th>
<th scope="col" class="col-1">@translator.Translate(userLanguage, "Urgency")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Metric")</th>
<th scope="col" class="@(hasRefresh ? "col-4" : "col-5")">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
@if (hasRefresh)
{
<th scope="col" class="col-1">Done</th>
<th scope="col" class="col-1">@translator.Translate(userLanguage, "Done")</th>
}
<th scope="col" class="col-1">Delete</th>
<th scope="col" class="col-1">@translator.Translate(userLanguage, "Delete")</th>
</tr>
</thead>
<tbody>
@@ -43,19 +48,19 @@
<tr class="d-flex" style="cursor:pointer;" onclick="showEditReminderRecordModal(@reminderRecord.Id)">
@if (reminderRecord.Urgency == ReminderUrgency.VeryUrgent)
{
<td class="col-1"><span class="badge text-bg-danger">Very Urgent</span></td>
<td class="col-1"><span class="badge text-bg-danger">@translator.Translate(userLanguage, "Very Urgent")</span></td>
}
else if (reminderRecord.Urgency == ReminderUrgency.Urgent)
{
<td class="col-1"><span class="badge text-bg-warning">Urgent</span></td>
<td class="col-1"><span class="badge text-bg-warning">@translator.Translate(userLanguage, "Urgent")</span></td>
}
else if (reminderRecord.Urgency == ReminderUrgency.PastDue)
{
<td class="col-1"><span class="badge text-bg-secondary">Past Due</span></td>
<td class="col-1"><span class="badge text-bg-secondary">@translator.Translate(userLanguage, "Past Due")</span></td>
}
else
{
<td class="col-1"><span class="badge text-bg-success">Not Urgent</span></td>
<td class="col-1"><span class="badge text-bg-success">@translator.Translate(userLanguage, "Not Urgent")</span></td>
}
@if (reminderRecord.Metric == ReminderMetric.Date)
{

View File

@@ -1,4 +1,11 @@
@model ReportViewModel
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model ReportViewModel
<div class="container reportTabContainer">
<div class="row hideOnPrint">
<div class="col-md-3 col-12 mt-2">
@@ -25,23 +32,23 @@
<div class="col-12 col-md-10 reportsCheckBoxContainer">
<div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="serviceExpenseCheck" value="1" checked>
<label class="form-check-label" for="serviceExpenseCheck">Service</label>
<label class="form-check-label" for="serviceExpenseCheck">@translator.Translate(userLanguage,"Service")</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="repairExpenseCheck" value="2" checked>
<label class="form-check-label" for="repairExpenseCheck">Repairs</label>
<label class="form-check-label" for="repairExpenseCheck">@translator.Translate(userLanguage, "Repairs")</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="upgradeExpenseCheck" value="3" checked>
<label class="form-check-label" for="upgradeExpenseCheck">Upgrades</label>
<label class="form-check-label" for="upgradeExpenseCheck">@translator.Translate(userLanguage, "Upgrades")</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="gasExpenseCheck" value="4" checked>
<label class="form-check-label" for="gasExpenseCheck">Fuel</label>
<label class="form-check-label" for="gasExpenseCheck">@translator.Translate(userLanguage, "Fuel")</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="taxExpenseCheck" value="5" checked>
<label class="form-check-label" for="taxExpenseCheck">Taxes</label>
<label class="form-check-label" for="taxExpenseCheck">@translator.Translate(userLanguage, "Taxes")</label>
</div>
</div>
<div class="col-md-1 d-sm-none d-md-block"></div>
@@ -56,10 +63,10 @@
<div class="row">
<div class="col-12">
<select class="form-select" onchange="updateReminderPie()" id="reminderOption">
<option value="0">As of Today</option>
<option value="30">+30 Days</option>
<option value="60">+60 Days</option>
<option value="90">+90 Days</option>
<option value="0">@translator.Translate(userLanguage, "As of Today")</option>
<option value="30">@translator.Translate(userLanguage, "+30 Days")</option>
<option value="60">@translator.Translate(userLanguage, "+60 Days")</option>
<option value="90">@translator.Translate(userLanguage, "+90 Days")</option>
</select>
</div>
</div>
@@ -82,10 +89,10 @@
</div>
<div class="col-md-3 col-12 chartContainer">
<div class="d-grid">
<button onclick="generateVehicleHistoryReport()" class="btn btn-secondary btn-md mt-1 mb-1">Vehicle Maintenance Report<i class="bi ms-2 bi-box-arrow-in-up-right"></i></button>
<button onclick="generateVehicleHistoryReport()" class="btn btn-secondary btn-md mt-1 mb-1">@translator.Translate(userLanguage, "Vehicle Maintenance Report")<i class="bi ms-2 bi-box-arrow-in-up-right"></i></button>
</div>
<div class="d-grid">
<button onclick="exportAttachments()" class="btn btn-secondary btn-md mt-1 mb-1">Export Attachments<i class="bi ms-2 bi-box-arrow-in-up-right"></i></button>
<button onclick="exportAttachments()" class="btn btn-secondary btn-md mt-1 mb-1">@translator.Translate(userLanguage, "Export Attachments")<i class="bi ms-2 bi-box-arrow-in-up-right"></i></button>
</div>
</div>
</div>

View File

@@ -1,9 +1,14 @@
@model ServiceRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model ServiceRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Service Record" : "Edit Service Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Service Record") : translator.Translate(userLanguage,"Edit Service Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddServiceRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,22 +17,22 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="serviceRecordDate">Date</label>
<label for="serviceRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="serviceRecordDate" class="form-control" placeholder="Date service was performed" value="@Model.Date">
<input type="text" id="serviceRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date service was performed")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="serviceRecordMileage">Odometer</label>
<input type="number" id="serviceRecordMileage" class="form-control" placeholder="Odometer reading when serviced" value="@(isNew ? "" : Model.Mileage)">
<label for="serviceRecordDescription">Description</label>
<input type="text" id="serviceRecordDescription" class="form-control" placeholder="Description of item(s) serviced(i.e. Oil Change)" value="@Model.Description">
<label for="serviceRecordCost">Cost</label>
<input type="text" id="serviceRecordCost" class="form-control" placeholder="Cost of the service" value="@(isNew ? "" : Model.Cost)">
<label for="serviceRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="serviceRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when serviced")" value="@(isNew ? "" : Model.Mileage)">
<label for="serviceRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="serviceRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of item(s) serviced(i.e. Oil Change)")" value="@Model.Description">
<label for="serviceRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="serviceRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of the service")" value="@(isNew ? "" : Model.Cost)">
@if (isNew)
{
@await Html.PartialAsync("_SupplyStore", "ServiceRecord")
}
<label for="serviceRecordTag">Tags(optional)</label>
<label for="serviceRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="serviceRecordTag">
@foreach(string tag in Model.Tags)
{
@@ -36,15 +41,15 @@
</select>
</div>
<div class="col-md-6 col-12">
<label for="serviceRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="serviceRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="serviceRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="serviceRecordFiles">Upload more documents</label>
<label for="serviceRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="serviceRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
@@ -54,13 +59,13 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck">
Add Reminder
@translator.Translate(userLanguage,"Add Reminder")
</label>
</div>
}
<label for="serviceRecordFiles">Upload documents(optional)</label>
<label for="serviceRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="serviceRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -71,25 +76,25 @@
@if (!isNew)
{
<div class="btn-group" style="margin-right:auto;">
<button type="button" class="btn btn-md mt-1 mb-1 btn-danger" onclick="deleteServiceRecord(@Model.Id)" >Delete</button>
<button type="button" class="btn btn-md mt-1 mb-1 btn-danger" onclick="deleteServiceRecord(@Model.Id)">@translator.Translate(userLanguage,"Delete")</button>
<button type="button" class="btn btn-md btn-danger btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><h6 class="dropdown-header">Move To</h6></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'ServiceRecord', 'RepairRecord')">Repairs</a></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'ServiceRecord', 'UpgradeRecord')">Upgrades</a></li>
<li><h6 class="dropdown-header">@translator.Translate(userLanguage,"Move To")</h6></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'ServiceRecord', 'RepairRecord')">@translator.Translate(userLanguage,"Repairs")</a></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'ServiceRecord', 'UpgradeRecord')">@translator.Translate(userLanguage,"Upgrades")</a></li>
</ul>
</div>
}
<button type="button" class="btn btn-secondary" onclick="hideAddServiceRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddServiceRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveServiceRecordToVehicle()">Add New Service Record</button>
<button type="button" class="btn btn-primary" onclick="saveServiceRecordToVehicle()">@translator.Translate(userLanguage,"Add New Service Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveServiceRecordToVehicle(true)">Edit Service Record</button>
<button type="button" class="btn btn-primary" onclick="saveServiceRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Service Record")</button>
}
</div>
<script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var userLanguage = userConfig.UserLanguage;
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
}
@model List<ServiceRecord>
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Service Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary">@($"Total: {Model.Sum(x => x.Cost).ToString("C")}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Service Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach(string recordTag in recordTags)
{
<span onclick="filterTable('servicerecord-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
@@ -26,21 +29,21 @@
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddServiceRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Service Record</button>
<button onclick="showAddServiceRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Service Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('ServiceRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('ServiceRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('ServiceRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('ServiceRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage,"Print")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddServiceRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Service Record</button>
<button onclick="showAddServiceRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Service Record")</button>
}
</div>
</div>
@@ -55,11 +58,11 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th>
<th scope="col" class="col-2">Odometer</th>
<th scope="col" class="col-3 col-xl-4">Description</th>
<th scope="col" class="col-2" onclick="toggleSort('servicerecord-tab-pane', this)" style="cursor:pointer;">Cost</th>
<th scope="col" class="col-3">Notes</th>
<th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" onclick="toggleSort('servicerecord-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
</tr>
</thead>
<tbody>
@@ -69,7 +72,7 @@
<td class="col-2 col-xl-1">@serviceRecord.Date.ToShortDateString()</td>
<td class="col-2">@serviceRecord.Mileage</td>
<td class="col-3 col-xl-4">@serviceRecord.Description</td>
<td class="col-2">@((hideZero && serviceRecord.Cost == default) ? "---" : serviceRecord.Cost.ToString("C"))</td>
<td class="col-2" data-record-type="cost">@((hideZero && serviceRecord.Cost == default) ? "---" : serviceRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(serviceRecord.Notes)</td>
</tr>
}

View File

@@ -1,9 +1,14 @@
@model SupplyRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model SupplyRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Supply Record" : "Edit Supply Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Supply Record") : translator.Translate(userLanguage,"Edit Supply Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddSupplyRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,45 +17,45 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="supplyRecordDate">Date</label>
<label for="supplyRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="supplyRecordDate" class="form-control" placeholder="Date purchased" value="@Model.Date">
<input type="text" id="supplyRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date purchased")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="supplyRecordPartNumber">Part Number</label>
<input type="text" id="supplyRecordPartNumber" class="form-control" placeholder="Part #/Model #/SKU #" value="@(isNew ? "" : Model.PartNumber)">
<label for="supplyRecordDescription">Description</label>
<input type="text" id="supplyRecordDescription" class="form-control" placeholder="Description of the Part/Supplies" value="@Model.Description">
<label for="supplyRecordSupplier">Supplier/Vendor</label>
<input type="text" id="supplyRecordSupplier" class="form-control" placeholder="Part Supplier" value="@Model.PartSupplier">
<label for="supplyRecordPartNumber">@translator.Translate(userLanguage,"Part Number")</label>
<input type="text" id="supplyRecordPartNumber" class="form-control" placeholder="@translator.Translate(userLanguage,"Part #/Model #/SKU #")" value="@(isNew ? "" : Model.PartNumber)">
<label for="supplyRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="supplyRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of the Part/Supplies")" value="@Model.Description">
<label for="supplyRecordSupplier">@translator.Translate(userLanguage,"Supplier/Vendor")</label>
<input type="text" id="supplyRecordSupplier" class="form-control" placeholder="@translator.Translate(userLanguage,"Part Supplier")" value="@Model.PartSupplier">
<div class="row">
<div class="col-md-6 col-12">
<label for="supplyRecordQuantity">Quantity</label>
<input type="text" id="supplyRecordQuantity" class="form-control" placeholder="Quantity" value="@(isNew ? "1" : Model.Quantity)">
<label for="supplyRecordQuantity">@translator.Translate(userLanguage,"Quantity")</label>
<input type="text" id="supplyRecordQuantity" class="form-control" placeholder="@translator.Translate(userLanguage,"Quantity")" value="@(isNew ? "1" : Model.Quantity)">
</div>
<div class="col-md-6 col-12">
<label for="supplyRecordCost">Cost</label>
<input type="text" id="supplyRecordCost" class="form-control" placeholder="Cost" value="@(isNew ? "" : Model.Cost)">
<label for="supplyRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="supplyRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost")" value="@(isNew ? "" : Model.Cost)">
</div>
</div>
</div>
<div class="col-md-6 col-12">
<label for="supplyRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="supplyRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="supplyRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="supplyRecordFiles">Upload more documents</label>
<label for="supplyRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="supplyRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
{
<label for="supplyRecordFiles">Upload documents(optional)</label>
<label for="supplyRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="supplyRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -60,16 +65,16 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteSupplyRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deleteSupplyRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage,"Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddSupplyRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddSupplyRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveSupplyRecordToVehicle()">Add New Supply Record</button>
<button type="button" class="btn btn-primary" onclick="saveSupplyRecordToVehicle()">@translator.Translate(userLanguage,"Add New Supply Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveSupplyRecordToVehicle(true)">Edit Supply Record</button>
<button type="button" class="btn btn-primary" onclick="saveSupplyRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Supply Record")</button>
}
</div>
<script>

View File

@@ -1,35 +1,38 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
}
@model List<SupplyRecord>
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Supply Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary">@($"Total: {Model.Sum(x => x.Cost).ToString("C")}")</span>
<span class="ms-2 badge bg-success">@($"{translator.Translate(userLanguage,"# of Supply Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
</div>
<div>
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddSupplyRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Supply Record</button>
<button onclick="showAddSupplyRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Supply Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('SupplyRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('SupplyRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('SupplyRecord')">@translator.Translate(userLanguage,"Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('SupplyRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage,"Print")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddSupplyRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Supply Record</button>
<button onclick="showAddSupplyRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage,"Add Supply Record")</button>
}
</div>
</div>
@@ -44,13 +47,13 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th>
<th scope="col" class="col-2">Part #</th>
<th scope="col" class="col-2">Supplier</th>
<th scope="col" class="col-2 col-xl-3">Description</th>
<th scope="col" class="col-1" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">Quantity</th>
<th scope="col" class="col-1" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">Cost</th>
<th scope="col" class="col-2">Notes</th>
<th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Part #")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Supplier")</th>
<th scope="col" class="col-2 col-xl-3">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-1" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Quantity")</th>
<th scope="col" class="col-1" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Notes")</th>
</tr>
</thead>
<tbody>

View File

@@ -1,7 +1,14 @@
@model string
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model string
<div class="row">
<div class="col-12">
<a onclick="showSuppliesModal()" class="btn btn-link">Choose Supplies</a>
<a onclick="showSuppliesModal()" class="btn btn-link">@translator.Translate(userLanguage,"Choose Supplies")</a>
</div>
</div>
<script>

View File

@@ -1,6 +1,13 @@
@model List<SupplyRecord>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model List<SupplyRecord>
<div class="modal-header">
<h5 class="modal-title">Select Supplies</h5>
<h5 class="modal-title">@translator.Translate(userLanguage,"Select Supplies")</h5>
<button type="button" class="btn-close" onclick="hideSuppliesModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -9,17 +16,16 @@
<div class="row">
<div class="col-12" style="max-height:50vh; overflow-y:auto;">
<div class="alert alert-warning" role="alert">
Supplies are requisitioned immediately after the record is created and cannot be modified.
If you have incorrectly entered the amount you needed you will need to correct it in the Supplies tab.
@translator.Translate(userLanguage,"Supplies are requisitioned immediately after the record is created and cannot be modified. If you have incorrectly entered the amount you needed you will need to correct it in the Supplies tab.")
</div>
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-1"></th>
<th scope="col" class="col-2">Quantity.</th>
<th scope="col" class="col-2">In Stock</th>
<th scope="col" class="col-5">Description</th>
<th scope="col" class="col-2">Unit Cost</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage,"Quantity")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "In Stock")</th>
<th scope="col" class="col-5">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Unit Cost")</th>
</tr>
</thead>
<tbody>
@@ -43,7 +49,7 @@
<div class="row">
<div class="col-12">
<div class="text-center">
<h4>No supplies with quantities greater than 0 is found.</h4>
<h4>@translator.Translate(userLanguage, "No supplies with quantities greater than 0 is found.")</h4>
</div>
</div>
</div>
@@ -51,8 +57,8 @@
</div>
<div class="modal-footer">
<span id="supplySumLabel" style="margin-right:auto;">Total: 0.00</span>
<button type="button" class="btn btn-secondary" onclick="hideSuppliesModal()">Cancel</button>
<button type="button" class="btn btn-primary" disabled id="selectSuppliesButton" onclick="selectSupplies()">Select</button>
<button type="button" class="btn btn-secondary" onclick="hideSuppliesModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" class="btn btn-primary" disabled id="selectSuppliesButton" onclick="selectSupplies()">@translator.Translate(userLanguage, "Select")</button>
</div>
<script>
function recalculateTotal() {

View File

@@ -1,9 +1,14 @@
@model TaxRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model TaxRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Tax Record" : "Edit Tax Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Tax Record") : translator.Translate(userLanguage,"Edit Tax Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddTaxRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,16 +17,16 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="taxRecordDate">Date</label>
<label for="taxRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="taxRecordDate" class="form-control" placeholder="Date tax was paid" value="@Model.Date">
<input type="text" id="taxRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date tax was paid")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="taxRecordDescription">Description</label>
<input type="text" id="taxRecordDescription" class="form-control" placeholder="Description of tax paid(i.e. Registration)" value="@Model.Description">
<label for="taxRecordCost">Cost</label>
<input type="text" id="taxRecordCost" class="form-control" placeholder="Cost of tax paid" value="@(isNew? "" : Model.Cost)">
<label for="taxRecordTag">Tags(optional)</label>
<label for="taxRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="taxRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of tax paid(i.e. Registration)")" value="@Model.Description">
<label for="taxRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="taxRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of tax paid")" value="@(isNew? "" : Model.Cost)">
<label for="taxRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="taxRecordTag">
@foreach (string tag in Model.Tags)
{
@@ -30,29 +35,29 @@
</select>
</div>
<div class="col-md-6 col-12">
<label for="taxRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="taxRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<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>
<label class="form-check-label" for="taxIsRecurring">@translator.Translate(userLanguage,"Is Recurring")</label>
</div>
<label for="taxRecurringMonth">Month</label>
<label for="taxRecurringMonth">@translator.Translate(userLanguage,"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>
<!option value="OneMonth" @(Model.RecurringInterval == ReminderMonthInterval.OneMonth ? "selected" : "")>@translator.Translate(userLanguage,"1 Month")</!option>
<!option value="ThreeMonths" @(Model.RecurringInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>@translator.Translate(userLanguage, "3 Months")</!option>
<!option value="SixMonths" @(Model.RecurringInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>@translator.Translate(userLanguage, "6 Months")</!option>
<!option value="OneYear" @(Model.RecurringInterval == ReminderMonthInterval.OneYear ? "selected" : "")>@translator.Translate(userLanguage, "1 Year")</!option>
<!option value="TwoYears" @(Model.RecurringInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>@translator.Translate(userLanguage, "2 Years")</!option>
<!option value="ThreeYears" @(Model.RecurringInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>@translator.Translate(userLanguage, "3 Years")</!option>
<!option value="FiveYears" @(Model.RecurringInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>@translator.Translate(userLanguage, "5 Years")</!option>
</select>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="taxRecordFiles">Upload more documents</label>
<label for="taxRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="taxRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
@@ -62,13 +67,13 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck">
Add Reminder
@translator.Translate(userLanguage,"Add Reminder")
</label>
</div>
}
<label for="taxRecordFiles">Upload documents(optional)</label>
<label for="taxRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="taxRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -78,16 +83,16 @@
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteTaxRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
<button type="button" class="btn btn-danger" onclick="deleteTaxRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage,"Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddTaxRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddTaxRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveTaxRecordToVehicle()">Add New Tax Record</button>
<button type="button" class="btn btn-primary" onclick="saveTaxRecordToVehicle()">@translator.Translate(userLanguage,"Add New Tax Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveTaxRecordToVehicle(true)">Edit Tax Record</button>
<button type="button" class="btn btn-primary" onclick="saveTaxRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Tax Record")</button>
}
</div>
<script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
}
@model List<TaxRecord>
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Tax Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary">@($"Total: {Model.Sum(x => x.Cost).ToString("C")}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Tax Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="filterTable('tax-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
@@ -26,21 +29,21 @@
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddTaxRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Tax Record</button>
<button onclick="showAddTaxRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Tax Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('TaxRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('TaxRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('TaxRecord')">@translator.Translate(userLanguage, "Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('TaxRecord')">@translator.Translate(userLanguage, "Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage, "Print")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddTaxRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Tax Record</button>
<button onclick="showAddTaxRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Tax Record")</button>
}
</div>
</div>
@@ -55,10 +58,10 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-3 col-xl-1">Date</th>
<th scope="col" class="col-4 col-xl-6">Description</th>
<th scope="col" class="col-2" onclick="toggleSort('tax-tab-pane', this)" style="cursor:pointer;">Cost</th>
<th scope="col" class="col-3">Notes</th>
<th scope="col" class="col-3 col-xl-1">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-4 col-xl-6">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" onclick="toggleSort('tax-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
</tr>
</thead>
<tbody>
@@ -67,7 +70,7 @@
<tr class="d-flex" style="cursor:pointer;" onclick="showEditTaxRecordModal(@taxRecord.Id)" data-tags='@string.Join(" ", taxRecord.Tags)'>
<td class="col-3 col-xl-1">@taxRecord.Date.ToShortDateString()</td>
<td class="col-4 col-xl-6">@taxRecord.Description</td>
<td class="col-2">@((hideZero && taxRecord.Cost == default) ? "---" : taxRecord.Cost.ToString("C"))</td>
<td class="col-2" data-record-type="cost">@((hideZero && taxRecord.Cost == default) ? "---" : taxRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(taxRecord.Notes)</td>
</tr>
}

View File

@@ -1,9 +1,14 @@
@model UpgradeRecordInput
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model UpgradeRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew ? "Add New Upgrade Record" : "Edit Upgrade Record")</h5>
<h5 class="modal-title">@(isNew ? translator.Translate(userLanguage,"Add New Upgrade Record") : translator.Translate(userLanguage,"Edit Upgrade Record"))</h5>
<button type="button" class="btn-close" onclick="hideAddUpgradeRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -12,22 +17,22 @@
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="upgradeRecordDate">Date</label>
<label for="upgradeRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group">
<input type="text" id="upgradeRecordDate" class="form-control" placeholder="Date upgrade/mods was installed" value="@Model.Date">
<input type="text" id="upgradeRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date upgrade/mods was installed")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="upgradeRecordMileage">Odometer</label>
<input type="number" id="upgradeRecordMileage" class="form-control" placeholder="Odometer reading when upgraded/modded" value="@(isNew ? "" : Model.Mileage)">
<label for="upgradeRecordDescription">Description</label>
<input type="text" id="upgradeRecordDescription" class="form-control" placeholder="Description of item(s) upgraded/modded" value="@Model.Description">
<label for="upgradeRecordCost">Cost</label>
<input type="text" id="upgradeRecordCost" class="form-control" placeholder="Cost of the upgrade/mods" value="@(isNew ? "" : Model.Cost)">
<label for="upgradeRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="upgradeRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when upgraded/modded")" value="@(isNew ? "" : Model.Mileage)">
<label for="upgradeRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="upgradeRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of item(s) upgraded/modded")" value="@Model.Description">
<label for="upgradeRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="upgradeRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of the upgrade/mods")" value="@(isNew ? "" : Model.Cost)">
@if (isNew)
{
@await Html.PartialAsync("_SupplyStore", "UpgradeRecord")
}
<label for="upgradeRecordTag">Tags(optional)</label>
<label for="upgradeRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="upgradeRecordTag">
@foreach (string tag in Model.Tags)
{
@@ -36,15 +41,15 @@
</select>
</div>
<div class="col-md-6 col-12">
<label for="upgradeRecordNotes">Notes(optional)<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<label for="upgradeRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="upgradeRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="upgradeRecordFiles">Upload more documents</label>
<label for="upgradeRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="upgradeRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
</div>
}
else
@@ -54,13 +59,13 @@
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck">
Add Reminder
@translator.Translate(userLanguage,"Add Reminder")
</label>
</div>
}
<label for="upgradeRecordFiles">Upload documents(optional)</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="upgradeRecordFiles">
<br /><small class="text-body-secondary">Max File Size: 28.6MB</small>
<br /><small class="text-body-secondary">@translator.Translate(userLanguage,"Max File Size: 28.6MB")</small>
}
</div>
</div>
@@ -71,25 +76,25 @@
@if (!isNew)
{
<div class="btn-group" style="margin-right:auto;">
<button type="button" class="btn btn-md mt-1 mb-1 btn-danger" onclick="deleteUpgradeRecord(@Model.Id)">Delete</button>
<button type="button" class="btn btn-md mt-1 mb-1 btn-danger" onclick="deleteUpgradeRecord(@Model.Id)">@translator.Translate(userLanguage,"Delete")</button>
<button type="button" class="btn btn-md btn-danger btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><h6 class="dropdown-header">Move To</h6></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'UpgradeRecord', 'ServiceRecord')">Service Records</a></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'UpgradeRecord', 'RepairRecord')">Repairs</a></li>
<li><h6 class="dropdown-header">@translator.Translate(userLanguage,"Move To")</h6></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'UpgradeRecord', 'ServiceRecord')">@translator.Translate(userLanguage,"Service Records")</a></li>
<li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'UpgradeRecord', 'RepairRecord')">@translator.Translate(userLanguage,"Repairs")</a></li>
</ul>
</div>
}
<button type="button" class="btn btn-secondary" onclick="hideAddUpgradeRecordModal()">Cancel</button>
<button type="button" class="btn btn-secondary" onclick="hideAddUpgradeRecordModal()">@translator.Translate(userLanguage,"Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveUpgradeRecordToVehicle()">Add New Upgrade Record</button>
<button type="button" class="btn btn-primary" onclick="saveUpgradeRecordToVehicle()">@translator.Translate(userLanguage,"Add New Upgrade Record")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveUpgradeRecordToVehicle(true)">Edit Upgrade Record</button>
<button type="button" class="btn btn-primary" onclick="saveUpgradeRecordToVehicle(true)">@translator.Translate(userLanguage,"Edit Upgrade Record")</button>
}
</div>
<script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports;
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
}
@model List<UpgradeRecord>
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success">@($"# of Upgrade Records: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary">@($"Total: {Model.Sum(x => x.Cost).ToString("C")}")</span>
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Upgrade Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="filterTable('upgrade-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
@@ -26,21 +29,21 @@
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showAddUpgradeRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Upgrade Record</button>
<button onclick="showAddUpgradeRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Upgrade Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('UpgradeRecord')">Import via CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('UpgradeRecord')">Export to CSV</a></li>
<li><a class="dropdown-item" href="#" onclick="showBulkImportModal('UpgradeRecord')">@translator.Translate(userLanguage, "Import via CSV")</a></li>
<li><a class="dropdown-item" href="#" onclick="exportVehicleData('UpgradeRecord')">@translator.Translate(userLanguage, "Export to CSV")</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">Print</a></li>
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage, "Print")</a></li>
</ul>
</div>
}
else
{
<button onclick="showAddUpgradeRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Upgrade Record</button>
<button onclick="showAddUpgradeRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Upgrade Record")</button>
}
</div>
</div>
@@ -55,11 +58,11 @@
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th>
<th scope="col" class="col-2">Odometer</th>
<th scope="col" class="col-3 col-xl-4">Description</th>
<th scope="col" class="col-2" onclick="toggleSort('upgrade-tab-pane', this)" style="cursor:pointer;">Cost</th>
<th scope="col" class="col-3">Notes</th>
<th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" onclick="toggleSort('upgrade-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
</tr>
</thead>
<tbody>
@@ -69,7 +72,7 @@
<td class="col-2 col-xl-1">@upgradeRecord.Date.ToShortDateString()</td>
<td class="col-2">@upgradeRecord.Mileage</td>
<td class="col-3 col-xl-4">@upgradeRecord.Description</td>
<td class="col-2">@((hideZero && upgradeRecord.Cost == default) ? "---" : upgradeRecord.Cost.ToString("C"))</td>
<td class="col-2" data-record-type="cost">@((hideZero && upgradeRecord.Cost == default) ? "---" : upgradeRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(upgradeRecord.Notes)</td>
</tr>
}

View File

@@ -1,5 +1,12 @@
@model List<UploadedFiles>
<label>Uploaded Documents</label>
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model List<UploadedFiles>
<label>@translator.Translate(userLanguage, "Uploaded Documents")</label>
<ul class="list-group">
@foreach (UploadedFiles filesUploaded in Model)
{

View File

@@ -1,14 +1,17 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var hideZero = config.GetUserConfig(User).HideZero;
var userConfig = config.GetUserConfig(User);
var hideZero = userConfig.HideZero;
var userLanguage = userConfig.UserLanguage;
}
@model VehicleHistoryViewModel
<div class="vehicleDetailTabContainer">
<div class="row mt-2">
<div class="d-flex">
<img src="/defaults/lubelogger_logo.png" />
<span class="display-6 ms-5">Vehicle Maintenance Report</span>
<span class="display-6 ms-5">@translator.Translate(userLanguage, "Vehicle Maintenance Report")</span>
</div>
</div>
<hr />
@@ -24,21 +27,21 @@
<li class="list-group-item">
@if (Model.VehicleData.IsElectric)
{
<span><i class="bi bi-ev-station"></i> Electric</span>
<span><i class="bi bi-ev-station me-2"></i>@translator.Translate(userLanguage, "Electric")</span>
}
else
{
<span><i class="bi bi-fuel-pump"></i> Gasoline</span>
<span><i class="bi bi-fuel-pump me-2"></i>@translator.Translate(userLanguage, "Gasoline")</span>
}
</li>
</ul>
</div>
<div class="col-6">
<ul class="list-group">
<li class="list-group-item">Last Reported Odometer Reading: @Model.Odometer</li>
<li class="list-group-item">Average Fuel Economy: @($"{Model.MPG}")</li>
<li class="list-group-item">Total Spent(excl. fuel): @Model.TotalCost.ToString("C")</li>
<li class="list-group-item">Total Spent on Fuel: @Model.TotalGasCost.ToString("C")</li>
<li class="list-group-item">@($"{translator.Translate(userLanguage, "Last Reported Odometer Reading")}: {Model.Odometer}") </li>
<li class="list-group-item">@($"{translator.Translate(userLanguage, "Average Fuel Economy")}: {Model.MPG}") </li>
<li class="list-group-item">@($"{translator.Translate(userLanguage, "Total Spent(excl. fuel)")}: {Model.TotalCost.ToString("C")}") </li>
<li class="list-group-item">@($"{translator.Translate(userLanguage, "Total Spent on Fuel")}: {Model.TotalGasCost.ToString("C")}") </li>
</ul>
</div>
</div>
@@ -48,12 +51,12 @@
<table class="table table-hover">
<thead>
<tr class="d-flex">
<th scope="col" class="col-2 servicehistorytype">Type</th>
<th scope="col" class="col-1 servicehistorydate">Date</th>
<th scope="col" class="col-1">Odometer</th>
<th scope="col" class="col-3">Description</th>
<th scope="col" class="col-1">Cost</th>
<th scope="col" class="col-4">Notes</th>
<th scope="col" class="col-2 servicehistorytype">@translator.Translate(userLanguage, "Type")</th>
<th scope="col" class="col-1 servicehistorydate">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-1">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-1">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-4">@translator.Translate(userLanguage, "Notes")</th>
</tr>
</thead>
<tbody>
@@ -61,18 +64,21 @@
{
<tr class="d-flex">
<td class="col-2 servicehistorytype">
@if(reportData.DataType == ImportMode.ServiceRecord)
@if (reportData.DataType == ImportMode.ServiceRecord)
{
<span><i class="bi bi-card-checklist me-2"></i>Service</span>
} else if (reportData.DataType == ImportMode.RepairRecord)
<span><i class="bi bi-card-checklist me-2"></i>@translator.Translate(userLanguage, "Service")</span>
}
else if (reportData.DataType == ImportMode.RepairRecord)
{
<span><i class="bi bi-exclamation-octagon me-2"></i>Repair</span>
} else if (reportData.DataType == ImportMode.UpgradeRecord)
<span><i class="bi bi-exclamation-octagon me-2"></i>@translator.Translate(userLanguage, "Repair")</span>
}
else if (reportData.DataType == ImportMode.UpgradeRecord)
{
<span><i class="bi bi-wrench-adjustable me-2"></i>Upgrade</span>
} else if (reportData.DataType == ImportMode.TaxRecord)
<span><i class="bi bi-wrench-adjustable me-2"></i>@translator.Translate(userLanguage, "Upgrade")</span>
}
else if (reportData.DataType == ImportMode.TaxRecord)
{
<span><i class="bi bi-currency-dollar me-2"></i>Tax</span>
<span><i class="bi bi-currency-dollar me-2"></i>@translator.Translate(userLanguage, "Tax")</span>
}
</td>
<td class="col-1 servicehistorydate">@reportData.Date.ToShortDateString()</td>

View File

@@ -1,5 +1,10 @@
@model Vehicle
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model Vehicle
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var isNew = Model.Id == 0;
if (Model.ImageLocation == "/defaults/noimage.png")
{
@@ -7,7 +12,7 @@
}
}
<div class="modal-header">
<h5 class="modal-title" id="addVehicleModalLabel">@(isNew ? "Add New Vehicle" : "Edit Vehicle")</h5>
<h5 class="modal-title" id="addVehicleModalLabel">@(isNew ? translator.Translate(userLanguage, "Add New Vehicle") : translator.Translate(userLanguage, "Edit Vehicle"))</h5>
@if (isNew)
{
<button type="button" class="btn-close" onclick="hideAddVehicleModal()" aria-label="Close"></button>
@@ -21,23 +26,23 @@
<div class="modal-body">
<form class="form-inline">
<div class="form-group">
<label for="inputYear">Year</label>
<input type="number" id="inputYear" class="form-control" placeholder="Year(must be after 1900)" value="@(isNew ? "" : Model.Year)">
<label for="inputMake">Make</label>
<input type="text" id="inputMake" class="form-control" placeholder="Make" value="@Model.Make">
<label for="inputModel">Model</label>
<input type="text" id="inputModel" class="form-control" placeholder="Model" value="@Model.Model">
<label for="inputLicensePlate">License Plate</label>
<input type="text" id="inputLicensePlate" class="form-control" placeholder="License Plate" value="@Model.LicensePlate">
<label for="inputYear">@translator.Translate(userLanguage, "Year")</label>
<input type="number" id="inputYear" class="form-control" placeholder="@translator.Translate(userLanguage, "Year(must be after 1900)")" value="@(isNew ? "" : Model.Year)">
<label for="inputMake">@translator.Translate(userLanguage, "Make")</label>
<input type="text" id="inputMake" class="form-control" placeholder="@translator.Translate(userLanguage, "Make")" value="@Model.Make">
<label for="inputModel">@translator.Translate(userLanguage, "Model")</label>
<input type="text" id="inputModel" class="form-control" placeholder="@translator.Translate(userLanguage, "Model")" value="@Model.Model">
<label for="inputLicensePlate">@translator.Translate(userLanguage, "License Plate")</label>
<input type="text" id="inputLicensePlate" class="form-control" placeholder="@translator.Translate(userLanguage, "License Plate")" value="@Model.LicensePlate">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="inputIsElectric" checked="@Model.IsElectric">
<label class="form-check-label" for="inputIsElectric">Electric Vehicle</label>
<label class="form-check-label" for="inputIsElectric">@translator.Translate(userLanguage, "Electric Vehicle")</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="inputUseHours" checked="@Model.UseHours">
<label class="form-check-label" for="inputUseHours">Use Engine Hours</label>
<label class="form-check-label" for="inputUseHours">@translator.Translate(userLanguage, "Use Engine Hours")</label>
</div>
<label for="inputTag">Tags(optional)</label>
<label for="inputTag">@translator.Translate(userLanguage, "Tags(optional)")</label>
<select multiple class="form-select" id="inputTag">
@foreach (string tag in Model.Tags)
{
@@ -46,11 +51,11 @@
</select>
@if (!string.IsNullOrWhiteSpace(Model.ImageLocation))
{
<label for="inputImage">Replace picture(optional)</label>
<label for="inputImage">@translator.Translate(userLanguage, "Replace picture(optional)")</label>
<input onChange="uploadFileAsync(this)" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
} else
{
<label for="inputImage">Upload a picture(optional)</label>
<label for="inputImage">@translator.Translate(userLanguage, "Upload a picture(optional)")</label>
<input onChange="uploadFileAsync(this)" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
}
</div>
@@ -59,12 +64,12 @@
<div class="modal-footer">
@if (isNew)
{
<button type="button" class="btn btn-secondary" onclick="hideAddVehicleModal()">Cancel</button>
<button type="button" onclick="saveVehicle(false)" class="btn btn-primary">Add New Vehicle</button>
<button type="button" class="btn btn-secondary" onclick="hideAddVehicleModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" onclick="saveVehicle(false)" class="btn btn-primary">@translator.Translate(userLanguage, "Add New Vehicle")</button>
} else if (!isNew)
{
<button type="button" class="btn btn-secondary" onclick="hideEditVehicleModal()">Cancel</button>
<button type="button" onclick="saveVehicle(true)" class="btn btn-primary">Save Vehicle</button>
<button type="button" class="btn btn-secondary" onclick="hideEditVehicleModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" onclick="saveVehicle(true)" class="btn btn-primary">@translator.Translate(userLanguage, "Save Vehicle")</button>
}
</div>
<script>

View File

@@ -19,6 +19,7 @@
"UseMarkDownOnSavedNotes": false,
"PreferredGasMileageUnit": "",
"PreferredGasUnit": "",
"UserLanguage": "en_US",
"VisibleTabs": [ 0, 1, 4, 2, 3, 6, 5, 8 ],
"DefaultTab": 8,
"UserNameHash": "",

View File

@@ -10,6 +10,7 @@ services:
volumes:
- config:/App/config
- data:/App/data
- translations:/App/wwwroot/translations
- documents:/App/wwwroot/documents
- images:/App/wwwroot/images
- temp:/App/wwwroot/temp
@@ -41,6 +42,7 @@ services:
volumes:
config:
data:
translations:
documents:
images:
temp:

View File

@@ -10,6 +10,7 @@ services:
volumes:
- config:/App/config
- data:/App/data
- translations:/App/wwwroot/translations
- documents:/App/wwwroot/documents
- images:/App/wwwroot/images
- temp:/App/wwwroot/temp
@@ -24,6 +25,7 @@ services:
volumes:
config:
data:
translations:
documents:
images:
temp:

File diff suppressed because one or more lines are too long

View File

@@ -45,7 +45,7 @@ function deleteCollisionRecord(collisionRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleCollisionRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -72,7 +72,7 @@ function saveCollisionRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -94,7 +94,7 @@ function getAndValidateCollisionRecordValues() {
} else {
$("#collisionRecordDate").removeClass("is-invalid");
}
if (collisionMileage.trim() == '' || parseInt(collisionMileage) < 0) {
if (collisionMileage.trim() == '' || isNaN(collisionMileage) || parseInt(collisionMileage) < 0) {
hasError = true;
$("#collisionRecordMileage").addClass("is-invalid");
} else {

View File

@@ -4,6 +4,7 @@
$("#gasRecordModalContent").html(data);
//initiate datepicker
initDatePicker($('#gasRecordDate'));
initTagSelector($("#gasRecordTag"));
$('#gasRecordModal').modal('show');
}
});
@@ -14,6 +15,7 @@ function showEditGasRecordModal(gasRecordId) {
$("#gasRecordModalContent").html(data);
//initiate datepicker
initDatePicker($('#gasRecordDate'));
initTagSelector($("#gasRecordTag"));
$('#gasRecordModal').modal('show');
$('#gasRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
if (getGlobalConfig().useMarkDown) {
@@ -43,7 +45,7 @@ function deleteGasRecord(gasRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleGasRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -67,7 +69,7 @@ function saveGasRecordToVehicle(isEdit) {
saveScrollPosition();
getVehicleGasRecords(formValues.vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -80,6 +82,7 @@ function getAndValidateGasRecordValues() {
var gasIsFillToFull = $("#gasIsFillToFull").is(":checked");
var gasIsMissed = $("#gasIsMissed").is(":checked");
var gasNotes = $("#gasRecordNotes").val();
var gasTags = $("#gasRecordTag").val();
var vehicleId = GetVehicleId().vehicleId;
var gasRecordId = getGasRecordModelData().id;
//validation
@@ -90,7 +93,7 @@ function getAndValidateGasRecordValues() {
} else {
$("#gasRecordDate").removeClass("is-invalid");
}
if (gasMileage.trim() == '' || parseInt(gasMileage) < 0) {
if (gasMileage.trim() == '' || isNaN(gasMileage) || parseInt(gasMileage) < 0) {
hasError = true;
$("#gasRecordMileage").addClass("is-invalid");
} else {
@@ -128,6 +131,7 @@ function getAndValidateGasRecordValues() {
gallons: gasGallons,
cost: gasCost,
files: uploadedFiles,
tags: gasTags,
isFillToFull: gasIsFillToFull,
missedFuelUp: gasIsMissed,
notes: gasNotes
@@ -254,20 +258,23 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
elem.innerText = convertedAmount.toFixed(2);
}
//update labels up top.
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().replace("Average Fuel Economy: ", ""));
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) {
newAverage = 100 / newAverage;
$("#averageFuelMileageLabel").text(`Average Fuel Economy: ${newAverage.toFixed(2)}`)
var averageLabel = $("#averageFuelMileageLabel");
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${newAverage.toFixed(2)}`);
}
var newMin = globalParseFloat($("#minFuelMileageLabel").text().replace("Min Fuel Economy: ", ""));
var newMin = globalParseFloat($("#minFuelMileageLabel").text().split(":")[1].trim());
if (newMin > 0) {
newMin = 100 / newMin;
$("#minFuelMileageLabel").text(`Min Fuel Economy: ${newMin.toFixed(2)}`)
var minLabel = $("#minFuelMileageLabel");
minLabel.text(`${minLabel.text().split(':')[0]}: ${newMin.toFixed(2)}`);
}
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().replace("Max Fuel Economy: ", ""));
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().split(":")[1].trim());
if (newMax > 0) {
newMax = 100 / newMax;
$("#maxFuelMileageLabel").text(`Max Fuel Economy: ${newMax.toFixed(2)}`)
var maxLabel = $("#maxFuelMileageLabel");
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${newMax.toFixed(2)}`);
}
sender.text(sender.text().replace(sender.attr("data-unit"), "km/l"));
sender.attr("data-unit", "km/l");
@@ -284,20 +291,23 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
convertedAmount = 100 / convertedAmount;
elem.innerText = convertedAmount.toFixed(2);
}
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().replace("Average Fuel Economy: ", ""));
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) {
newAverage = 100 / newAverage;
$("#averageFuelMileageLabel").text(`Average Fuel Economy: ${newAverage.toFixed(2)}`)
var averageLabel = $("#averageFuelMileageLabel");
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${newAverage.toFixed(2)}`);
}
var newMin = globalParseFloat($("#minFuelMileageLabel").text().replace("Min Fuel Economy: ", ""));
var newMin = globalParseFloat($("#minFuelMileageLabel").text().split(":")[1].trim());
if (newMin > 0) {
newMin = 100 / newMin;
$("#minFuelMileageLabel").text(`Min Fuel Economy: ${newMin.toFixed(2)}`)
var minLabel = $("#minFuelMileageLabel");
minLabel.text(`${minLabel.text().split(':')[0]}: ${newMin.toFixed(2)}`);
}
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().replace("Max Fuel Economy: ", ""));
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().split(":")[1].trim());
if (newMax > 0) {
newMax = 100 / newMax;
$("#maxFuelMileageLabel").text(`Max Fuel Economy: ${newMax.toFixed(2)}`)
var maxLabel = $("#maxFuelMileageLabel");
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${newMax.toFixed(2)}`);
}
sender.text(sender.text().replace(sender.attr("data-unit"), "l/100km"));
sender.attr("data-unit", "l/100km");
@@ -307,6 +317,34 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
}
}
}
function toggleGasFilter(sender) {
filterTable('gas-tab-pane', sender);
updateMPGLabels();
}
function updateMPGLabels() {
var averageLabel = $("#averageFuelMileageLabel");
var minLabel = $("#minFuelMileageLabel");
var maxLabel = $("#maxFuelMileageLabel");
if (averageLabel.length > 0 && minLabel.length > 0 && maxLabel.length > 0) {
var rowsToAggregate = $("[data-aggregated='true']").parent(":not('.override-hide')");
var rowMPG = rowsToAggregate.children('[data-gas-type="fueleconomy"]').toArray().map(x => globalParseFloat(x.textContent));
var maxMPG = rowMPG.length > 0 ? rowMPG.reduce((a, b) => a > b ? a : b) : 0;
var minMPG = rowMPG.length > 0 ? rowMPG.filter(x=>x>0).reduce((a, b) => a < b ? a : b) : 0;
var totalMilesTraveled = rowMPG.length > 0 ? rowsToAggregate.children('[data-gas-type="mileage"]').toArray().map(x => globalParseFloat($(x).attr("data-gas-aggregate"))).reduce((a, b) => a + b) : 0;
var totalGasConsumed = rowMPG.length > 0 ? rowsToAggregate.children('[data-gas-type="consumption"]').toArray().map(x => globalParseFloat($(x).attr("data-gas-aggregate"))).reduce((a, b) => a + b) : 0;
if (totalGasConsumed > 0) {
var averageMPG = totalMilesTraveled / totalGasConsumed;
if (!getGlobalConfig().useMPG && $("[data-gas='fueleconomy']").attr("data-unit") != 'km/l' && averageMPG > 0) {
averageMPG = 100 / averageMPG;
}
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${averageMPG.toFixed(2)}`);
} else {
averageLabel.text(`${averageLabel.text().split(':')[0]}: 0.00`);
}
minLabel.text(`${minLabel.text().split(':')[0]}: ${minMPG.toFixed(2)}`);
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${maxMPG.toFixed(2)}`);
}
}
function toggleUnits(sender) {
event.preventDefault();

View File

@@ -4,7 +4,13 @@
var isPersistent = $("#inputPersistent").is(":checked");
$.post('/Login/Login', {userName: userName, password: userPassword, isPersistent: isPersistent}, function (data) {
if (data) {
//check for redirectURL
var redirectURL = getRedirectURL().url;
if (redirectURL.trim() != "") {
window.location.href = redirectURL;
} else {
window.location.href = '/Home';
}
} else {
errorToast("Invalid Login Credentials, please try again.");
}

View File

@@ -41,7 +41,7 @@ function deleteNote(noteId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleNotes(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -65,7 +65,7 @@ function saveNoteToVehicle(isEdit) {
saveScrollPosition();
getVehicleNotes(formValues.vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -96,6 +96,7 @@ function getAndValidateNoteValues() {
vehicleId: vehicleId,
description: noteDescription,
noteText: noteText,
files: uploadedFiles,
pinned: noteIsPinned,
tags: noteTags
}

View File

@@ -45,7 +45,7 @@ function deleteOdometerRecord(odometerRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleOdometerRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -72,7 +72,7 @@ function saveOdometerRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -91,7 +91,7 @@ function getAndValidateOdometerRecordValues() {
} else {
$("#odometerRecordDate").removeClass("is-invalid");
}
if (serviceMileage.trim() == '' || parseInt(serviceMileage) < 0) {
if (serviceMileage.trim() == '' || isNaN(serviceMileage) || parseInt(serviceMileage) < 0) {
hasError = true;
$("#odometerRecordMileage").addClass("is-invalid");
} else {

View File

@@ -43,7 +43,7 @@ function deletePlanRecord(planRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehiclePlanRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -70,7 +70,7 @@ function savePlanRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -163,7 +163,7 @@ function updatePlanRecordProgress(newProgress) {
var vehicleId = GetVehicleId().vehicleId;
getVehiclePlanRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}
@@ -176,7 +176,7 @@ function updatePlanRecordProgress(newProgress) {
var vehicleId = GetVehicleId().vehicleId;
getVehiclePlanRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
draggedId = 0;

View File

@@ -64,7 +64,7 @@ function deleteReminderRecord(reminderRecordId, e) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleReminders(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -88,7 +88,7 @@ function saveReminderRecordToVehicle(isEdit) {
saveScrollPosition();
getVehicleReminders(formValues.vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -123,7 +123,7 @@ function markDoneReminderRecord(reminderRecordId, e) {
successToast("Reminder Updated");
getVehicleReminders(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}
@@ -143,7 +143,7 @@ function getAndValidateReminderRecordValues() {
//validation
var hasError = false;
var reminderDateIsInvalid = reminderDate.trim() == ''; //eliminates whitespace.
var reminderMileageIsInvalid = reminderMileage.trim() == '' || parseInt(reminderMileage) < 0;
var reminderMileageIsInvalid = reminderMileage.trim() == '' || isNaN(reminderMileage) || parseInt(reminderMileage) < 0;
if ((reminderOption == "Both" || reminderOption == "Date") && reminderDateIsInvalid) {
hasError = true;
$("#reminderDate").addClass("is-invalid");

View File

@@ -45,7 +45,7 @@ function deleteServiceRecord(serviceRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleServiceRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -72,7 +72,7 @@ function saveServiceRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -94,7 +94,7 @@ function getAndValidateServiceRecordValues() {
} else {
$("#serviceRecordDate").removeClass("is-invalid");
}
if (serviceMileage.trim() == '' || parseInt(serviceMileage) < 0) {
if (serviceMileage.trim() == '' || isNaN(serviceMileage) || parseInt(serviceMileage) < 0) {
hasError = true;
$("#serviceRecordMileage").addClass("is-invalid");
} else {

View File

@@ -92,7 +92,7 @@ function saveVehicle(isEdit) {
viewVehicle(vehicleId);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
}
@@ -244,14 +244,17 @@ function filterTable(tabName, sender, isSort) {
rowData.removeClass('override-hide');
$(sender).removeClass('bg-primary');
$(sender).addClass('bg-secondary');
updateAggregateLabels();
} else {
rowData.addClass('override-hide');
$(`[data-tags~='${tagName}']`).removeClass('override-hide');
updateAggregateLabels();
}
} else {
//hide table rows.
rowData.addClass('override-hide');
$(`[data-tags~='${tagName}']`).removeClass('override-hide');
updateAggregateLabels();
if ($(".tagfilter.bg-primary").length > 0) {
//disabling other filters
$(".tagfilter.bg-primary").addClass('bg-secondary');
@@ -261,3 +264,16 @@ function filterTable(tabName, sender, isSort) {
$(sender).removeClass('bg-secondary');
}
}
function updateAggregateLabels() {
//Sum
var sumLabel = $("[data-aggregate-type='sum']");
if (sumLabel.length > 0) {
var newSum = $("[data-record-type='cost']").parent(":not('.override-hide')").children("[data-record-type='cost']").toArray().map(x => globalParseFloat(x.textContent)).reduce((a, b,) => a + b).toFixed(2);
sumLabel.text(`${sumLabel.text().split(':')[0]}: ${getGlobalConfig().currencySymbol}${newSum}`)
}
//Count
var newCount = $("[data-record-type='cost']").parent(":not('.override-hide')").length;
var countLabel = $("[data-aggregate-type='count']");
countLabel.text(`${countLabel.text().split(':')[0]}: ${newCount}`)
}

View File

@@ -43,7 +43,7 @@ function deleteSupplyRecord(supplyRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleSupplyRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -70,7 +70,7 @@ function saveSupplyRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}

View File

@@ -53,7 +53,7 @@ function deleteTaxRecord(taxRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleTaxRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -80,7 +80,7 @@ function saveTaxRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}

View File

@@ -45,7 +45,7 @@ function deleteUpgradeRecord(upgradeRecordId) {
var vehicleId = GetVehicleId().vehicleId;
getVehicleUpgradeRecords(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {
@@ -72,7 +72,7 @@ function saveUpgradeRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500);
}
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
})
}
@@ -94,7 +94,7 @@ function getAndValidateUpgradeRecordValues() {
} else {
$("#upgradeRecordDate").removeClass("is-invalid");
}
if (upgradeMileage.trim() == '' || parseInt(upgradeMileage) < 0) {
if (upgradeMileage.trim() == '' || isNaN(upgradeMileage) || parseInt(upgradeMileage) < 0) {
hasError = true;
$("#upgradeRecordMileage").addClass("is-invalid");
} else {

View File

@@ -72,7 +72,7 @@ $(document).ready(function () {
$("#plan-tab-pane").html("");
break;
case "odometer-tab":
$("odometer-tab-pane").html("");
$("#odometer-tab-pane").html("");
break;
}
});
@@ -226,7 +226,7 @@ function exportVehicleData(mode) {
var vehicleId = GetVehicleId().vehicleId;
$.get('/Vehicle/ExportFromVehicleToCsv', { vehicleId: vehicleId, mode: mode }, function (data) {
if (!data) {
errorToast("An error occurred, please try again later");
errorToast(genericErrorMessage());
} else {
window.location.href = data;
}
@@ -408,7 +408,7 @@ function moveRecord(recordId, source, dest) {
var vehicleId = GetVehicleId().vehicleId;
refreshDataCallBack(vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
errorToast(genericErrorMessage());
}
});
} else {