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,34 +2,50 @@ name: Build and Push Image to Dockerhub and GHCR
on: on:
push: push:
branches: [ "main" ] branches: ["main"]
release:
types: ["published"]
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 - name: Set up Docker Buildx
- name: Login to DockerHub uses: docker/setup-buildx-action@v3
uses: docker/login-action@v3
with: - name: Login to DockerHub
uses: docker/login-action@v3
with:
username: "${{ secrets.DH_USER }}" username: "${{ secrets.DH_USER }}"
password: "${{ secrets.DH_PASS }}" password: "${{ secrets.DH_PASS }}"
- name: Login to GitHub Container Registry
uses: docker/login-action@v3 - name: Login to GitHub Container Registry
with: uses: docker/login-action@v3
with:
registry: ghcr.io registry: ghcr.io
username: "hargata" username: "hargata"
password: "${{ secrets.GHCR_PAT }}" password: "${{ secrets.GHCR_PAT }}"
- name: Build and push
uses: docker/build-push-action@v5 - name: Docker Metadata
with: 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: . context: .
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: | tags: ${{ steps.meta.outputs.tags }}
hargata/lubelogger:latest labels: ${{ steps.meta.outputs.labels }}
ghcr.io/hargata/lubelogger:latest

1
.gitignore vendored
View File

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

View File

@@ -33,6 +33,26 @@ namespace CarCareTracker.Controllers
return Json(fileName); 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] [HttpPost]
public IActionResult HandleMultipleFileUpload(List<IFormFile> file) public IActionResult HandleMultipleFileUpload(List<IFormFile> file)
{ {
@@ -44,7 +64,7 @@ namespace CarCareTracker.Controllers
} }
return Json(uploadedFiles); return Json(uploadedFiles);
} }
[Authorize(Roles = nameof(UserData.IsRootUser))]
[HttpPost] [HttpPost]
public IActionResult DeleteFiles(string fileLocation) public IActionResult DeleteFiles(string fileLocation)
{ {

View File

@@ -15,17 +15,20 @@ namespace CarCareTracker.Controllers
private readonly ILogger<HomeController> _logger; private readonly ILogger<HomeController> _logger;
private readonly IVehicleDataAccess _dataAccess; private readonly IVehicleDataAccess _dataAccess;
private readonly IUserLogic _userLogic; private readonly IUserLogic _userLogic;
private readonly IFileHelper _fileHelper;
private readonly IConfigHelper _config; private readonly IConfigHelper _config;
public HomeController(ILogger<HomeController> logger, public HomeController(ILogger<HomeController> logger,
IVehicleDataAccess dataAccess, IVehicleDataAccess dataAccess,
IUserLogic userLogic, IUserLogic userLogic,
IConfigHelper configuration) IConfigHelper configuration,
IFileHelper fileHelper)
{ {
_logger = logger; _logger = logger;
_dataAccess = dataAccess; _dataAccess = dataAccess;
_config = configuration; _config = configuration;
_userLogic = userLogic; _userLogic = userLogic;
_fileHelper = fileHelper;
} }
private int GetUserID() private int GetUserID()
{ {
@@ -47,7 +50,13 @@ namespace CarCareTracker.Controllers
public IActionResult Settings() public IActionResult Settings()
{ {
var userConfig = _config.GetUserConfig(User); 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] [HttpPost]
public IActionResult WriteToSettings(UserConfig userConfig) public IActionResult WriteToSettings(UserConfig userConfig)

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ namespace CarCareTracker.Helper
UserConfig GetUserConfig(ClaimsPrincipal user); UserConfig GetUserConfig(ClaimsPrincipal user);
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData); bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
string GetLogoUrl(); string GetLogoUrl();
string GetServerLanguage();
public bool DeleteUserConfig(int userId); public bool DeleteUserConfig(int userId);
} }
public class ConfigHelper : IConfigHelper public class ConfigHelper : IConfigHelper
@@ -34,6 +35,11 @@ namespace CarCareTracker.Helper
} }
return logoUrl; return logoUrl;
} }
public string GetServerLanguage()
{
var serverLanguage = _config[nameof(UserConfig.UserLanguage)] ?? "en_US";
return serverLanguage;
}
public bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData) public bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData)
{ {
var storedUserId = user.FindFirstValue(ClaimTypes.NameIdentifier); var storedUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);
@@ -110,6 +116,7 @@ namespace CarCareTracker.Helper
EnableAutoOdometerInsert = bool.Parse(_config[nameof(UserConfig.EnableAutoOdometerInsert)]), EnableAutoOdometerInsert = bool.Parse(_config[nameof(UserConfig.EnableAutoOdometerInsert)]),
PreferredGasMileageUnit = _config[nameof(UserConfig.PreferredGasMileageUnit)], PreferredGasMileageUnit = _config[nameof(UserConfig.PreferredGasMileageUnit)],
PreferredGasUnit = _config[nameof(UserConfig.PreferredGasUnit)], PreferredGasUnit = _config[nameof(UserConfig.PreferredGasUnit)],
UserLanguage = _config[nameof(UserConfig.UserLanguage)],
VisibleTabs = _config.GetSection("VisibleTabs").Get<List<ImportMode>>(), VisibleTabs = _config.GetSection("VisibleTabs").Get<List<ImportMode>>(),
DefaultTab = (ImportMode)int.Parse(_config[nameof(UserConfig.DefaultTab)]) 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 GetFullFilePath(string currentFilePath, bool mustExist = true);
string MoveFileFromTemp(string currentFilePath, string newFolder); string MoveFileFromTemp(string currentFilePath, string newFolder);
bool RenameFile(string currentFilePath, string newName);
bool DeleteFile(string currentFilePath); bool DeleteFile(string currentFilePath);
string MakeBackup(); string MakeBackup();
bool RestoreBackup(string fileName, bool clearExisting = false); bool RestoreBackup(string fileName, bool clearExisting = false);
string MakeAttachmentsExport(List<GenericReportModel> exportData); string MakeAttachmentsExport(List<GenericReportModel> exportData);
List<string> GetLanguages();
} }
public class FileHelper : IFileHelper public class FileHelper : IFileHelper
{ {
@@ -21,6 +23,40 @@ namespace CarCareTracker.Helper
_webEnv = webEnv; _webEnv = webEnv;
_logger = logger; _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) public string GetFullFilePath(string currentFilePath, bool mustExist = true)
{ {
if (currentFilePath.StartsWith("/")) if (currentFilePath.StartsWith("/"))
@@ -132,9 +168,9 @@ namespace CarCareTracker.Helper
if (!Directory.Exists(tempPath)) if (!Directory.Exists(tempPath))
Directory.CreateDirectory(tempPath); Directory.CreateDirectory(tempPath);
int fileIndex = 0; 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 fileToCopy = GetFullFilePath(file.Location);
var destFileName = $"{tempPath}/{fileIndex}{Path.GetExtension(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) public string GetAverageGasMileage(List<GasRecordViewModel> results, bool useMPG)
{ {
var recordsToCalculateGallons = results.Where(x => x.MilesPerGallon > 0 || !x.IsFillToFull); var recordsToCalculate = results.Where(x => x.IncludeInAverage);
var recordWithCalculatedMPG = recordsToCalculateGallons.Where(x => x.MilesPerGallon > 0); if (recordsToCalculate.Any())
var mileageAdjustment = results.Where(x => x.MissedFuelUp).Sum(y => y.DeltaMileage);
if (recordWithCalculatedMPG.Any())
{ {
var maxMileage = recordWithCalculatedMPG.Max(x => x.Mileage); try
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)
{ {
averageGasMileage = 100 / averageGasMileage; 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 averageGasMileage.ToString("F");
} }
return "0"; return "0";
} }
@@ -68,7 +70,8 @@ namespace CarCareTracker.Helper
CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0, CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0,
IsFillToFull = currentObject.IsFillToFull, IsFillToFull = currentObject.IsFillToFull,
MissedFuelUp = currentObject.MissedFuelUp, MissedFuelUp = currentObject.MissedFuelUp,
Notes = currentObject.Notes Notes = currentObject.Notes,
Tags = currentObject.Tags
}; };
if (currentObject.MissedFuelUp) if (currentObject.MissedFuelUp)
{ {
@@ -120,7 +123,8 @@ namespace CarCareTracker.Helper
CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0, CostPerGallon = convertedConsumption > 0.00M ? currentObject.Cost / convertedConsumption : 0,
IsFillToFull = currentObject.IsFillToFull, IsFillToFull = currentObject.IsFillToFull,
MissedFuelUp = currentObject.MissedFuelUp, MissedFuelUp = currentObject.MissedFuelUp,
Notes = currentObject.Notes Notes = currentObject.Notes,
Tags = currentObject.Tags
}); });
} }
previousMileage = currentObject.Mileage; 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.Progress).Name(["progress"]);
Map(m => m.Type).Name(["type"]); Map(m => m.Type).Name(["type"]);
Map(m => m.Priority).Name(["priority"]); Map(m => m.Priority).Name(["priority"]);
Map(m => m.Tags).Name(["tags"]);
} }
} }
} }

View File

@@ -157,7 +157,13 @@ namespace CarCareTracker.Middleware
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
Response.Redirect("/Login/Index"); 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; return Task.CompletedTask;
} }
protected override Task HandleForbiddenAsync(AuthenticationProperties properties) protected override Task HandleForbiddenAsync(AuthenticationProperties properties)

View File

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

View File

@@ -21,5 +21,7 @@
public bool IsFillToFull { get; set; } public bool IsFillToFull { get; set; }
public bool MissedFuelUp { get; set; } public bool MissedFuelUp { get; set; }
public string Notes { 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 PartNumber { get; set; }
public string PartSupplier { get; set; } public string PartSupplier { get; set; }
public string PartQuantity { get; set; } public string PartQuantity { get; set; }
public string Tags { get; set; }
} }
public class SupplyRecordExportModel public class SupplyRecordExportModel
@@ -43,12 +44,14 @@
public string Description { get; set; } public string Description { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public string Cost { get; set; } public string Cost { get; set; }
public string Tags { get; set; }
} }
public class OdometerRecordExportModel public class OdometerRecordExportModel
{ {
public string Date { get; set; } public string Date { get; set; }
public string Odometer { get; set; } public string Odometer { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public string Tags { get; set; }
} }
public class TaxRecordExportModel public class TaxRecordExportModel
{ {
@@ -56,6 +59,7 @@
public string Description { get; set; } public string Description { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public string Cost { get; set; } public string Cost { get; set; }
public string Tags { get; set; }
} }
public class GasRecordExportModel public class GasRecordExportModel
{ {
@@ -67,6 +71,7 @@
public string IsFillToFull { get; set; } public string IsFillToFull { get; set; }
public string MissedFuelUp { get; set; } public string MissedFuelUp { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public string Tags { get; set; }
} }
public class ReminderExportModel public class ReminderExportModel
{ {

View File

@@ -8,5 +8,6 @@
public string NoteText { get; set; } public string NoteText { get; set; }
public bool Pinned { get; set; } public bool Pinned { get; set; }
public List<string> Tags { get; set; } = new List<string>(); 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 PreferredGasMileageUnit { get; set; } = string.Empty;
public string UserNameHash { get; set; } public string UserNameHash { get; set; }
public string UserPasswordHash { get; set;} public string UserPasswordHash { get; set;}
public string UserLanguage { get; set; } = "en_US";
public List<ImportMode> VisibleTabs { get; set; } = new List<ImportMode>() { public List<ImportMode> VisibleTabs { get; set; } = new List<ImportMode>() {
ImportMode.Dashboard, ImportMode.Dashboard,
ImportMode.ServiceRecord, ImportMode.ServiceRecord,

View File

@@ -5,6 +5,8 @@ using CarCareTracker.Logic;
using CarCareTracker.Middleware; using CarCareTracker.Middleware;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -33,6 +35,7 @@ builder.Services.AddSingleton<IReminderHelper, ReminderHelper>();
builder.Services.AddSingleton<IReportHelper, ReportHelper>(); builder.Services.AddSingleton<IReportHelper, ReportHelper>();
builder.Services.AddSingleton<IMailHelper, MailHelper>(); builder.Services.AddSingleton<IMailHelper, MailHelper>();
builder.Services.AddSingleton<IConfigHelper, ConfigHelper>(); builder.Services.AddSingleton<IConfigHelper, ConfigHelper>();
builder.Services.AddSingleton<ITranslationHelper, TranslationHelper>();
//configure logic //configure logic
builder.Services.AddSingleton<ILoginLogic, LoginLogic>(); builder.Services.AddSingleton<ILoginLogic, LoginLogic>();
@@ -54,6 +57,17 @@ builder.Services.AddAuthorization(options =>
{ {
options.DefaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes("AuthN").RequireAuthenticatedUser().Build(); 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(); var app = builder.Build();

View File

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

View File

@@ -1,7 +1,10 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @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(); var logoUrl = config.GetLogoUrl();
} }
@model string @model string
@@ -14,21 +17,21 @@
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()"> <div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
<ul class="navbar-nav" id="homeTab" role="tablist"> <ul class="navbar-nav" id="homeTab" role="tablist">
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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> </li>
@if (User.IsInRole("CookieAuth")) @if (User.IsInRole("CookieAuth"))
{ {
@if (User.IsInRole(nameof(UserData.IsAdmin))) @if (User.IsInRole(nameof(UserData.IsAdmin)))
{ {
<li class="nav-item" role="presentation"> <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>
} }
<li class="nav-item" role="presentation"> <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> </li>
} }
</ul> </ul>
@@ -45,10 +48,10 @@
<hr /> <hr />
<ul class="nav nav-tabs lubelogger-tab" id="homeTab" role="tablist"> <ul class="nav nav-tabs lubelogger-tab" id="homeTab" role="tablist">
<li class="nav-item" role="presentation"> <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>
<li class="nav-item ms-auto" role="presentation"> <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> </li>
@if (User.IsInRole("CookieAuth")) @if (User.IsInRole("CookieAuth"))
{ {
@@ -58,11 +61,11 @@
@if (User.IsInRole(nameof(UserData.IsAdmin))) @if (User.IsInRole(nameof(UserData.IsAdmin)))
{ {
<li> <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>
} }
<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> </li>
</ul> </ul>
</li> </li>

View File

@@ -1,158 +1,185 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@model UserConfig @model SettingsViewModel
@inject ITranslationHelper translator
@{
var userLanguage = Model.UserConfig.UserLanguage;
}
<div class="row"> <div class="row">
<div class="d-flex justify-content-center"> <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> </div>
<hr /> <hr />
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<input id="preferredGasUnit" style="display:none;" value="@Model.PreferredGasUnit"/> <input id="preferredGasUnit" style="display:none;" value="@Model.UserConfig.PreferredGasUnit" />
<input id="preferredFuelMileageUnit" style="display:none;" value="@Model.PreferredGasMileageUnit" /> <input id="preferredFuelMileageUnit" style="display:none;" value="@Model.UserConfig.PreferredGasMileageUnit" />
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableDarkMode" checked="@Model.UseDarkMode"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableDarkMode" checked="@Model.UserConfig.UseDarkMode">
<label class="form-check-label" for="enableDarkMode">Dark Mode</label> <label class="form-check-label" for="enableDarkMode">@translator.Translate(userLanguage, "Dark Mode")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableCsvImports" checked="@Model.EnableCsvImports"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableCsvImports" checked="@Model.UserConfig.EnableCsvImports">
<label class="form-check-label" for="enableCsvImports">Enable CSV Imports</label> <label class="form-check-label" for="enableCsvImports">@translator.Translate(userLanguage, "Enable CSV Imports")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMPG" checked="@Model.UseMPG"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMPG" checked="@Model.UserConfig.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> <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>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useUKMPG" checked="@Model.UseUKMPG"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useUKMPG" checked="@Model.UserConfig.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> <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>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useDescending" checked="@Model.UseDescending"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useDescending" checked="@Model.UserConfig.UseDescending">
<label class="form-check-label" for="useDescending">Sort lists in Descending Order(Newest to Oldest)</label> <label class="form-check-label" for="useDescending">@translator.Translate(userLanguage, "Sort lists in Descending Order(Newest to Oldest)")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="hideZero" checked="@Model.HideZero"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="hideZero" checked="@Model.UserConfig.HideZero">
<label class="form-check-label" for="hideZero">Replace @(0.ToString("C")) Costs with ---</label> <label class="form-check-label" for="hideZero">@translator.Translate(userLanguage, "Replace $0.00 Costs with ---")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimal" checked="@Model.UseThreeDecimalGasCost"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimal" checked="@Model.UserConfig.UseThreeDecimalGasCost">
<label class="form-check-label" for="useThreeDecimal">Use Three Decimals For Fuel Cost</label> <label class="form-check-label" for="useThreeDecimal">@translator.Translate(userLanguage, "Use Three Decimals For Fuel Cost")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMarkDownOnSavedNotes" checked="@Model.UseMarkDownOnSavedNotes"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMarkDownOnSavedNotes" checked="@Model.UserConfig.UseMarkDownOnSavedNotes">
<label class="form-check-label" for="useMarkDownOnSavedNotes">Display Saved Notes in Markdown</label> <label class="form-check-label" for="useMarkDownOnSavedNotes">@translator.Translate(userLanguage, "Display Saved Notes in Markdown")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoReminderRefresh" checked="@Model.EnableAutoReminderRefresh"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoReminderRefresh" checked="@Model.UserConfig.EnableAutoReminderRefresh">
<label class="form-check-label" for="enableAutoReminderRefresh">Auto Refresh Lapsed Recurring Reminders</label> <label class="form-check-label" for="enableAutoReminderRefresh">@translator.Translate(userLanguage, "Auto Refresh Lapsed Recurring Reminders")</label>
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoOdometerInsert" checked="@Model.EnableAutoOdometerInsert"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoOdometerInsert" checked="@Model.UserConfig.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> <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> </div>
@if (User.IsInRole(nameof(UserData.IsRootUser))) @if (User.IsInRole(nameof(UserData.IsRootUser)))
{ {
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" onChange="enableAuthCheckChanged()" type="checkbox" role="switch" id="enableAuth" checked="@Model.EnableAuth"> <input class="form-check-input" onChange="enableAuthCheckChanged()" type="checkbox" role="switch" id="enableAuth" checked="@Model.UserConfig.EnableAuth">
<label class="form-check-label" for="enableAuth">Enable Authentication</label> <label class="form-check-label" for="enableAuth">@translator.Translate(userLanguage, "Enable Authentication")</label>
</div> </div>
} }
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<div class="row" id="visibleTabs"> <div class="row" id="visibleTabs">
<div class="col-12"> <div class="col-12">
<span class="lead">Visible Tabs</span> <span class="lead">@translator.Translate(userLanguage, "Visible Tabs")</span>
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <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" : "")> <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">Service Records</label> <label class="form-check-label stretched-link" for="serviceRecordTab">@translator.Translate(userLanguage, "Service Records")</label>
</li> </li>
<li class="list-group-item d-none"> <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" : "")> <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">Dashboard</label> <label class="form-check-label stretched-link" for="dashboardTab">@translator.Translate(userLanguage, "Dashboard")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Repairs</label> <label class="form-check-label stretched-link" for="repairRecordTab">@translator.Translate(userLanguage, "Repairs")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Upgrades</label> <label class="form-check-label stretched-link" for="upgradeRecordTab">@translator.Translate(userLanguage, "Upgrades")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Fuel</label> <label class="form-check-label stretched-link" for="gasRecordTab">@translator.Translate(userLanguage, "Fuel")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Odometer</label> <label class="form-check-label stretched-link" for="odometerRecordTab">@translator.Translate(userLanguage, "Odometer")</label>
</li> </li>
</ul> </ul>
</div> </div>
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item"> <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" : "")> <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">Taxes</label> <label class="form-check-label stretched-link" for="taxRecordTab">@translator.Translate(userLanguage, "Taxes")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Notes</label> <label class="form-check-label stretched-link" for="noteRecordTab">@translator.Translate(userLanguage, "Notes")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Reminder</label> <label class="form-check-label stretched-link" for="reminderRecordTab">@translator.Translate(userLanguage, "Reminder")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Supplies</label> <label class="form-check-label stretched-link" for="supplyRecordTab">@translator.Translate(userLanguage, "Supplies")</label>
</li> </li>
<li class="list-group-item"> <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" : "")> <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">Planner</label> <label class="form-check-label stretched-link" for="planRecordTab">@translator.Translate(userLanguage, "Planner")</label>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-12 col-md-6"> <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"> <select class="form-select" onchange="updateSettings()" id="defaultTab">
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.Dashboard)) value="Dashboard">Dashboard</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.Dashboard)) value="Dashboard">@translator.Translate(userLanguage, "Dashboard")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model,ImportMode.ServiceRecord)) value="ServiceRecord">Service Record</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.ServiceRecord)) value="ServiceRecord">@translator.Translate(userLanguage, "Service Record")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.RepairRecord)) value="RepairRecord">Repairs</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.RepairRecord)) value="RepairRecord">@translator.Translate(userLanguage, "Repairs")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.UpgradeRecord)) value="UpgradeRecord">Upgrades</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.UpgradeRecord)) value="UpgradeRecord">@translator.Translate(userLanguage, "Upgrades")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.GasRecord)) value="GasRecord">Fuel</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.GasRecord)) value="GasRecord">@translator.Translate(userLanguage, "Fuel")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.TaxRecord)) value="TaxRecord">Tax</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.TaxRecord)) value="TaxRecord">@translator.Translate(userLanguage, "Tax")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.NoteRecord)) value="NoteRecord">Notes</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.NoteRecord)) value="NoteRecord">@translator.Translate(userLanguage, "Notes")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.ReminderRecord)) value="ReminderRecord">Reminders</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.ReminderRecord)) value="ReminderRecord">@translator.Translate(userLanguage, "Reminders")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.SupplyRecord)) value="SupplyRecord">Supplies</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.SupplyRecord)) value="SupplyRecord">@translator.Translate(userLanguage, "Supplies")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.PlanRecord)) value="PlanRecord">Planner</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.PlanRecord)) value="PlanRecord">@translator.Translate(userLanguage, "Planner")</!option>
<!option @(StaticHelper.DefaultTabSelected(Model, ImportMode.OdometerRecord)) value="OdometerRecord">Odometer</!option> <!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.OdometerRecord)) value="OdometerRecord">@translator.Translate(userLanguage, "Odometer")</!option>
</select> </select>
</div> </div>
@if (User.IsInRole(nameof(UserData.IsRootUser))) <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"> <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="row">
<div class="col-6 d-grid"> <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>
<div class="col-6 d-grid"> <div class="col-6 d-grid">
<input onChange="restoreBackup(this)" type="file" accept=".zip" class="d-none" id="inputBackup"> <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>
<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>
</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> </div>
<div class="row"> <div class="row">
<div class="d-flex justify-content-center"> <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> </div>
<hr /> <hr />
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
@@ -173,7 +200,7 @@
<h6 class="display-7 mt-2">Hometown Shoutout</h6> <h6 class="display-7 mt-2">Hometown Shoutout</h6>
</div> </div>
<p class="lead"> <p class="lead">
Do you work remotely and are looking for a new place to call home? Consider looking into the rural Eastern Utah town of Price. Price and Carbon County Do you work remotely and are looking for a new place to call home? Consider looking into the rural Eastern Utah town of Price. Price and Carbon County
has experienced pronounced decline in both population and economic activity within the past decade whereas the rest of the state experienced exponential growth. has experienced pronounced decline in both population and economic activity within the past decade whereas the rest of the state experienced exponential growth.
It is conveniently located in between Salt Lake City and Moab Utah. Amenities are relatively complete in terms of big box stores and high speed fiber Internet. It is conveniently located in between Salt Lake City and Moab Utah. Amenities are relatively complete in terms of big box stores and high speed fiber Internet.
Price and its surrounding towns as a whole could really benefit from in-migration. Thank you! Price and its surrounding towns as a whole could really benefit from in-migration. Thank you!
@@ -204,10 +231,18 @@
}); });
return visibleTabs.toArray(); 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 visibleTabs = getCheckedTabs();
var defaultTab = $("#defaultTab").val(); var defaultTab = $("#defaultTab").val();
if (!visibleTabs.includes(defaultTab)){ if (!visibleTabs.includes(defaultTab)) {
defaultTab = "Dashboard"; //default to dashboard. defaultTab = "Dashboard"; //default to dashboard.
} }
var userConfigObject = { var userConfigObject = {
@@ -223,16 +258,17 @@
enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"), enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"),
preferredGasUnit: $("#preferredGasUnit").val(), preferredGasUnit: $("#preferredGasUnit").val(),
preferredGasMileageUnit: $("#preferredFuelMileageUnit").val(), preferredGasMileageUnit: $("#preferredFuelMileageUnit").val(),
userLanguage: $("#defaultLanguage").val(),
visibleTabs: visibleTabs, visibleTabs: visibleTabs,
defaultTab: defaultTab defaultTab: defaultTab
} }
sloader.show(); sloader.show();
$.post('/Home/WriteToSettings', { userConfig: userConfigObject}, function(data){ $.post('/Home/WriteToSettings', { userConfig: userConfigObject }, function (data) {
sloader.hide(); sloader.hide();
if (data) { if (data) {
setTimeout(function () { window.location.href = '/Home/Index?tab=settings' }, 500); setTimeout(function () { window.location.href = '/Home/Index?tab=settings' }, 500);
} else { } else {
errorToast("An error occurred, please try again later.") errorToast(genericErrorMessage());
} }
}) })
} }
@@ -241,9 +277,37 @@
window.location.href = data; window.location.href = data;
}); });
} }
function openRestoreBackup(){ function openUploadLanguage(){
$("#inputLanguage").click();
}
function openRestoreBackup() {
$("#inputBackup").click(); $("#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) { function restoreBackup(event) {
let formData = new FormData(); let formData = new FormData();
formData.append("file", event.files[0]); formData.append("file", event.files[0]);
@@ -257,29 +321,33 @@
type: 'POST', type: 'POST',
success: function (response) { success: function (response) {
if (response.trim() != '') { if (response.trim() != '') {
$.post('/Files/RestoreBackup', { fileName : response}, function (data) { $.post('/Files/RestoreBackup', { fileName: response }, function (data) {
sloader.hide(); sloader.hide();
if (data){ if (data) {
successToast("Backup Restored"); successToast("Backup Restored");
setTimeout(function () { window.location.href = '/Home/Index' }, 500); setTimeout(function () { window.location.href = '/Home/Index' }, 500);
} else { } 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"); var enableAuth = $("#enableAuth").is(":checked");
if (enableAuth) { if (enableAuth) {
//swal dialog to set up username and password. //swal dialog to set up username and password.
Swal.fire({ Swal.fire({
title: 'Setup Credentials', title: 'Setup Credentials',
html: ` html: `
<input type="text" id="authUsername" class="swal2-input" placeholder="Username"> <input type="text" id="authUsername" class="swal2-input" placeholder="Username">
<input type="password" id="authPassword" class="swal2-input" placeholder="Password"> <input type="password" id="authPassword" class="swal2-input" placeholder="Password">
`, `,
confirmButtonText: 'Setup', confirmButtonText: 'Setup',
focusConfirm: false, focusConfirm: false,
preConfirm: () => { preConfirm: () => {
@@ -296,7 +364,7 @@
if (data) { if (data) {
setTimeout(function () { window.location.href = '/Login' }, 500); setTimeout(function () { window.location.href = '/Login' }, 500);
} else { } else {
errorToast("An error occurred, please try again later."); errorToast(genericErrorMessage());
} }
}) })
} }
@@ -306,7 +374,7 @@
if (data) { if (data) {
setTimeout(function () { window.location.href = '/Home' }, 1000); setTimeout(function () { window.location.href = '/Home' }, 1000);
} else { } else {
errorToast("An error occurred, please try again later."); errorToast(genericErrorMessage());
} }
}); });
} }

View File

@@ -1,7 +1,9 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var logoUrl = config.GetLogoUrl(); var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
} }
@{ @{
ViewData["Title"] = "LubeLogger - Login"; ViewData["Title"] = "LubeLogger - Login";
@@ -14,17 +16,17 @@
<div class="col-12"> <div class="col-12">
<img src="@logoUrl" /> <img src="@logoUrl" />
<div class="form-group"> <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"> <input type="text" id="inputUserName" class="form-control">
</div> </div>
<div class="d-grid"> <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>
<div class="d-grid"> <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>
<div class="d-grid"> <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> </div>
</div> </div>

View File

@@ -1,7 +1,10 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@model string
@{ @{
var logoUrl = config.GetLogoUrl(); var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
} }
@{ @{
ViewData["Title"] = "LubeLogger - Login"; ViewData["Title"] = "LubeLogger - Login";
@@ -14,26 +17,31 @@
<div class="col-12"> <div class="col-12">
<img src="@logoUrl" /> <img src="@logoUrl" />
<div class="form-group"> <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"> <input type="text" id="inputUserName" class="form-control">
</div> </div>
<div class="form-group"> <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"> <input type="password" id="inputUserPassword" onkeyup="handlePasswordKeyPress(event)" class="form-control">
</div> </div>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="inputPersistent"> <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>
<div class="d-grid"> <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>
<div class="d-grid"> <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>
<div class="d-grid"> <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>
</div> </div>
</div> </div>
<script>
function getRedirectURL(){
return { url: decodeHTMLEntities('@Model') };
}
</script>

View File

@@ -1,7 +1,9 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var logoUrl = config.GetLogoUrl(); var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
} }
@{ @{
ViewData["Title"] = "LubeLogger - Register"; ViewData["Title"] = "LubeLogger - Register";
@@ -14,26 +16,26 @@
<div class="col-12"> <div class="col-12">
<img src="@logoUrl" /> <img src="@logoUrl" />
<div class="form-group"> <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"> <input type="text" id="inputToken" class="form-control">
</div> </div>
<div class="form-group"> <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"> <input type="text" id="inputEmail" class="form-control">
</div> </div>
<div class="form-group"> <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"> <input type="text" id="inputUserName" class="form-control">
</div> </div>
<div class="form-group"> <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"> <input type="password" id="inputUserPassword" class="form-control">
</div> </div>
<div class="d-grid"> <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>
<div class="d-grid"> <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> </div>
</div> </div>

View File

@@ -1,7 +1,9 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var logoUrl = config.GetLogoUrl(); var logoUrl = config.GetLogoUrl();
var userLanguage = config.GetServerLanguage();
} }
@{ @{
ViewData["Title"] = "LubeLogger - Register"; ViewData["Title"] = "LubeLogger - Register";
@@ -14,22 +16,22 @@
<div class="col-12"> <div class="col-12">
<img src="@logoUrl" /> <img src="@logoUrl" />
<div class="form-group"> <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"> <input type="text" id="inputToken" class="form-control">
</div> </div>
<div class="form-group"> <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"> <input type="text" id="inputEmail" class="form-control">
</div> </div>
<div class="form-group"> <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"> <input type="password" id="inputUserPassword" class="form-control">
</div> </div>
<div class="d-grid"> <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>
<div class="d-grid"> <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> </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 @using CarCareTracker.Helper
<!DOCTYPE html> <!DOCTYPE html>
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var userConfig = config.GetUserConfig(User); var userConfig = config.GetUserConfig(User);
var useDarkMode = userConfig.UseDarkMode; var useDarkMode = userConfig.UseDarkMode;
var enableCsvImports = userConfig.EnableCsvImports; var enableCsvImports = userConfig.EnableCsvImports;
var useMPG = userConfig.UseMPG;
var useMarkDown = userConfig.UseMarkDownOnSavedNotes; var useMarkDown = userConfig.UseMarkDownOnSavedNotes;
var useThreeDecimals = userConfig.UseThreeDecimalGasCost; var useThreeDecimals = userConfig.UseThreeDecimalGasCost;
var shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; var shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var numberFormat = System.Globalization.CultureInfo.CurrentCulture.NumberFormat; var numberFormat = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
var userLanguage = userConfig.UserLanguage;
shortDatePattern = shortDatePattern.ToLower(); shortDatePattern = shortDatePattern.ToLower();
if (!shortDatePattern.Contains("dd")) if (!shortDatePattern.Contains("dd"))
{ {
@@ -54,7 +57,8 @@
enableCsvImport : "@enableCsvImports" == "True", enableCsvImport : "@enableCsvImports" == "True",
useMarkDown: "@useMarkDown" == "True", useMarkDown: "@useMarkDown" == "True",
currencySymbol: decodeHTMLEntities("@numberFormat.CurrencySymbol"), currencySymbol: decodeHTMLEntities("@numberFormat.CurrencySymbol"),
useThreeDecimals: "@useThreeDecimals" == "True" useThreeDecimals: "@useThreeDecimals" == "True",
useMPG: "@useMPG" == "True"
} }
} }
function getShortDatePattern() { function getShortDatePattern() {
@@ -83,6 +87,9 @@
input = input.replace(".", decimalSeparator); input = input.replace(".", decimalSeparator);
return input; return input;
} }
function genericErrorMessage(){
return decodeHTMLEntities('@translator.Translate(userLanguage, "An error has occurred, please try again later")');
}
</script> </script>
@await RenderSectionAsync("Scripts", required: false) @await RenderSectionAsync("Scripts", required: false)
</head> </head>

View File

@@ -3,8 +3,10 @@
ViewData["Title"] = "LubeLogger - View Vehicle"; ViewData["Title"] = "LubeLogger - View Vehicle";
} }
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var userConfig = config.GetUserConfig(User); var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
@model Vehicle @model Vehicle
@section Scripts { @section Scripts {
@@ -26,46 +28,46 @@
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()"> <div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
<ul class="nav navbar-nav" id="vehicleTab" role="tablist"> <ul class="nav navbar-nav" id="vehicleTab" role="tablist">
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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> </li>
</ul> </ul>
</div> </div>
@@ -83,42 +85,42 @@
<hr /> <hr />
<ul class="nav nav-tabs lubelogger-tab" id="vehicleTab" role="tablist"> <ul class="nav nav-tabs lubelogger-tab" id="vehicleTab" role="tablist">
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item" role="presentation"> <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>
<li class="nav-item dropdown ms-auto" role="presentation"> <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"> <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> </ul>
</li> </li>
</ul> </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"> <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> <button type="button" class="btn-close" onclick="hideBulkImportModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -9,37 +16,40 @@
<div class="form-group"> <div class="form-group">
<div class="col-12"> <div class="col-12">
<div class="alert alert-warning" role="alert"> <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. @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.")
Dates must be supplied in a string.
Numbers must be supplied as numbers without currency formatting.
</div> </div>
<div class="alert alert-danger" role="alert"> <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> </div>
@if (Model == ImportMode.GasRecord) @if (Model == ImportMode.GasRecord)
{ {
<a class="btn btn-link" href="/defaults/gassample.csv" target="_blank">Download Sample</a> <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) }
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> <a class="btn btn-link" href="/defaults/servicerecordsample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
} else if (Model == ImportMode.TaxRecord) }
else if (Model == ImportMode.TaxRecord)
{ {
<a class="btn btn-link" href="/defaults/taxrecordsample.csv" target="_blank">Download Sample</a> <a class="btn btn-link" href="/defaults/taxrecordsample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
} else if (Model == ImportMode.SupplyRecord) }
else if (Model == ImportMode.SupplyRecord)
{ {
<a class="btn btn-link" href="/defaults/supplysample.csv" target="_blank">Download Sample</a> <a class="btn btn-link" href="/defaults/supplysample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
} else if (Model == ImportMode.PlanRecord) }
else if (Model == ImportMode.PlanRecord)
{ {
<a class="btn btn-link" href="/defaults/plansample.csv" target="_blank">Download Sample</a> <a class="btn btn-link" href="/defaults/plansample.csv" target="_blank">@translator.Translate(userLanguage, "Download Sample")</a>
} else if (Model == ImportMode.OdometerRecord) }
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> </div>
<div class="row mt-2"> <div class="row mt-2">
<div class="col-12"> <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"> <input onChange="uploadFileAsync(this)" type="file" multiple accept=".csv" class="form-control-file" id="csvFileUploader">
</div> </div>
</div> </div>
@@ -47,8 +57,8 @@
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hideBulkImportModal()">Cancel</button> <button type="button" class="btn btn-secondary" onclick="hideBulkImportModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" onclick="importFromCsv()" class="btn btn-primary">Import</button> <button type="button" onclick="importFromCsv()" class="btn btn-primary">@translator.Translate(userLanguage, "Import")</button>
</div> </div>
<script> <script>
var uploadedFile = ""; var uploadedFile = "";
@@ -73,13 +83,13 @@
getVehicleUpgradeRecords(vehicleId); getVehicleUpgradeRecords(vehicleId);
} else if (mode == "SupplyRecord") { } else if (mode == "SupplyRecord") {
getVehicleSupplyRecords(vehicleId); getVehicleSupplyRecords(vehicleId);
} else if (mode == "PlanRecord"){ } else if (mode == "PlanRecord") {
getVehiclePlanRecords(vehicleId); getVehiclePlanRecords(vehicleId);
} else if (mode == "OdometerRecord") { } else if (mode == "OdometerRecord") {
getVehicleOdometerRecords(vehicleId); getVehicleOdometerRecords(vehicleId);
} }
} else { } 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="row">
<div class="col-8"> <div class="col-8">
<span class="lead">Collaborators</span> <span class="lead">@translator.Translate(userLanguage, "Collaborators")</span>
</div> </div>
<div class="col-4"> <div class="col-4">
<button onclick="addCollaborator()" class="btn btn-link btn-sm"><i class="bi bi-person-add"></i></button> <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"> <table class="table table-hover">
<thead> <thead>
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-8">Username</th> <th scope="col" class="col-8">@translator.Translate(userLanguage, "Username")</th>
<th scope="col" class="col-4">Delete</th> <th scope="col" class="col-4">@translator.Translate(userLanguage, "Delete")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -37,7 +44,7 @@
if (data) { if (data) {
refreshCollaborators(); refreshCollaborators();
} else { } 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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddCollisionRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,22 +17,22 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="collisionRecordDate">Date</label> <label for="collisionRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="collisionRecordMileage">Odometer</label> <label for="collisionRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="collisionRecordMileage" class="form-control" placeholder="Odometer reading when repaired" value="@(isNew ? "" : Model.Mileage)"> <input type="number" id="collisionRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when repaired")" value="@(isNew ? "" : Model.Mileage)">
<label for="collisionRecordDescription">Description</label> <label for="collisionRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="collisionRecordDescription" class="form-control" placeholder="Description of item(s) repaired(i.e. Alternator)" value="@Model.Description"> <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">Cost</label> <label for="collisionRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="collisionRecordCost" class="form-control" placeholder="Cost of the repair" value="@(isNew ? "" : Model.Cost)"> <input type="text" id="collisionRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of the repair")" value="@(isNew ? "" : Model.Cost)">
@if (isNew) @if (isNew)
{ {
@await Html.PartialAsync("_SupplyStore", "RepairRecord") @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"> <select multiple class="form-select" id="collisionRecordTag">
@foreach (string tag in Model.Tags) @foreach (string tag in Model.Tags)
{ {
@@ -36,15 +41,15 @@
</select> </select>
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="collisionRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else else
@@ -54,13 +59,13 @@
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck"> <input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck"> <label class="form-check-label" for="addReminderCheck">
Add Reminder @translator.Translate(userLanguage,"Add Reminder")
</label> </label>
</div> </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"> <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>
</div> </div>
@@ -71,25 +76,25 @@
@if (!isNew) @if (!isNew)
{ {
<div class="btn-group" style="margin-right:auto;"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><h6 class="dropdown-header">Move To</h6></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')">Service Records</a></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')">Upgrades</a></li> <li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'RepairRecord', 'UpgradeRecord')">@translator.Translate(userLanguage,"Upgrades")</a></li>
</ul> </ul>
</div> </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) @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) 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> </div>
<script> <script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct(); var recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userLanguage = userConfig.UserLanguage;
} }
@model List<CollisionRecord> @model List<CollisionRecord>
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# 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-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags) @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> <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) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('RepairRecord')">@translator.Translate(userLanguage,"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="exportVehicleData('RepairRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} }
else 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>
</div> </div>
@@ -55,11 +58,11 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th> <th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-2">Odometer</th> <th scope="col" class="col-2">@translator.Translate(userLanguage,"Odometer")</th>
<th scope="col" class="col-3 col-xl-4">Description</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;">Cost</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">Notes</th> <th scope="col" class="col-3">@translator.Translate(userLanguage,"Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -69,7 +72,7 @@
<td class="col-2 col-xl-1">@collisionRecord.Date.ToShortDateString()</td> <td class="col-2 col-xl-1">@collisionRecord.Date.ToShortDateString()</td>
<td class="col-2">@collisionRecord.Mileage</td> <td class="col-2">@collisionRecord.Mileage</td>
<td class="col-3 col-xl-4">@collisionRecord.Description</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> <td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(collisionRecord.Notes)</td>
</tr> </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) @if (Model.CollisionRecordSum + Model.ServiceRecordSum + Model.GasRecordSum + Model.TaxRecordSum + Model.UpgradeRecordSum > 0)
{ {
<canvas id="pie-chart"></canvas> <canvas id="pie-chart"></canvas>
@@ -9,10 +16,14 @@
new Chart($("#pie-chart"), { new Chart($("#pie-chart"), {
type: 'pie', type: 'pie',
data: { 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: [ datasets: [
{ {
label: "Expenses by Type", label: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Type")'),
backgroundColor: ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"], backgroundColor: ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"],
data: [ data: [
globalParseFloat('@Model.ServiceRecordSum'), globalParseFloat('@Model.ServiceRecordSum'),
@@ -20,7 +31,7 @@
globalParseFloat('@Model.UpgradeRecordSum'), globalParseFloat('@Model.UpgradeRecordSum'),
globalParseFloat('@Model.TaxRecordSum'), globalParseFloat('@Model.TaxRecordSum'),
globalParseFloat('@Model.GasRecordSum') globalParseFloat('@Model.GasRecordSum')
] ]
} }
] ]
}, },
@@ -34,7 +45,7 @@
}, },
title: { title: {
display: true, display: true,
text: "Expenses by Type", text: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Type")'),
color: useDarkMode ? "#fff" : "#000" color: useDarkMode ? "#fff" : "#000"
}, },
} }
@@ -46,6 +57,6 @@
else else
{ {
<div class="text-center"> <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> </div>
} }

View File

@@ -1,6 +1,7 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject IGasHelper gasHelper @inject IGasHelper gasHelper
@inject ITranslationHelper translator
@model GasRecordViewModelContainer @model GasRecordViewModelContainer
@{ @{
var userConfig = config.GetUserConfig(User); var userConfig = config.GetUserConfig(User);
@@ -10,8 +11,10 @@
var hideZero = userConfig.HideZero; var hideZero = userConfig.HideZero;
var useThreeDecimals = userConfig.UseThreeDecimalGasCost; var useThreeDecimals = userConfig.UseThreeDecimalGasCost;
var gasCostFormat = useThreeDecimals ? "C3" : "C2"; var gasCostFormat = useThreeDecimals ? "C3" : "C2";
var userLanguage = userConfig.UserLanguage;
var useKwh = Model.UseKwh; var useKwh = Model.UseKwh;
var useHours = Model.UseHours; var useHours = Model.UseHours;
var recordTags = Model.GasRecords.SelectMany(x => x.Tags).Distinct();
string preferredFuelEconomyUnit = userConfig.PreferredGasMileageUnit; string preferredFuelEconomyUnit = userConfig.PreferredGasMileageUnit;
string preferredGasUnit = userConfig.PreferredGasUnit; string preferredGasUnit = userConfig.PreferredGasUnit;
string consumptionUnit; string consumptionUnit;
@@ -41,39 +44,49 @@
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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()) @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) 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="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">@($"Max 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.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
} else } 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="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">@($"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="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">@($"{translator.Translate(userLanguage,"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" 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> </div>
@if (enableCsvImports) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('GasRecord')">@translator.Translate(userLanguage,"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="exportVehicleData('GasRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} else { } 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>
</div> </div>
@@ -87,23 +100,23 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2">Date Refueled</th> <th scope="col" class="col-2">@translator.Translate(userLanguage,"Date Refueled")</th>
<th scope="col" class="col-2">Odometer(@(distanceUnit))</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;">Consumption(@(consumptionUnit))</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;">Fuel Economy(@(fuelEconomyUnit))</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;">Cost</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;">Unit Cost</th> <th scope="col" class="col-1" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage,"Unit Cost")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (GasRecordViewModel gasRecord in Model.GasRecords) @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.Date</td>
<td class="col-2">@gasRecord.Mileage</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">@gasRecord.Gallons.ToString("F")</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">@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.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">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</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> <td class="col-1" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td>
</tr> </tr>
} }
@@ -130,4 +143,5 @@
{ {
@:convertGasConsumptionUnits(decodeHTMLEntities('@consumptionUnit'), decodeHTMLEntities('@preferredGasUnit'), false); @:convertGasConsumptionUnits(decodeHTMLEntities('@consumptionUnit'), decodeHTMLEntities('@preferredGasUnit'), false);
} }
</script> </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 barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
var sortedByMPG = Model.OrderBy(x => x.Cost).ToList(); var sortedByMPG = Model.OrderBy(x => x.Cost).ToList();
} }
@@ -27,7 +32,7 @@
labels: barGraphLabels, labels: barGraphLabels,
datasets: [ datasets: [
{ {
label: "Expenses by Month", label: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Month")'),
backgroundColor: barGraphColors, backgroundColor: barGraphColors,
data: barGraphData data: barGraphData
} }
@@ -38,7 +43,7 @@
title: { title: {
display: true, display: true,
color: useDarkMode ? "#fff" : "#000", color: useDarkMode ? "#fff" : "#000",
text: 'Expenses by Month' text: decodeHTMLEntities('@translator.Translate(userLanguage, "Expenses by Month")')
}, },
legend: { legend: {
display: false, display: false,
@@ -67,6 +72,6 @@
} else } else
{ {
<div class="text-center"> <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> </div>
} }

View File

@@ -1,9 +1,12 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@model GasRecordInputContainer @model GasRecordInputContainer
@{ @{
var useMPG = config.GetUserConfig(User).UseMPG; var userConfig = config.GetUserConfig(User);
var useUKMPG = config.GetUserConfig(User).UseUKMPG; var useMPG = userConfig.UseMPG;
var useUKMPG = userConfig.UseUKMPG;
var userLanguage = userConfig.UserLanguage;
var useKwh = Model.UseKwh; var useKwh = Model.UseKwh;
var useHours = Model.UseHours; var useHours = Model.UseHours;
var isNew = Model.GasRecord.Id == 0; var isNew = Model.GasRecord.Id == 0;
@@ -34,7 +37,7 @@
} }
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddGasRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -43,57 +46,64 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="gasRecordDate">Date</label> <label for="gasRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="gasRecordMileage">Odometer Reading(@distanceUnit)</label> <label for="gasRecordMileage">@($"{translator.Translate(userLanguage,"Odometer Reading")}({distanceUnit})")</label>
<input type="number" id="gasRecordMileage" class="form-control" placeholder="Odometer reading when refueled" value="@(isNew ? "" : Model.GasRecord.Mileage)"> <input type="number" id="gasRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when refueled")" value="@(isNew ? "" : Model.GasRecord.Mileage)">
<label for="gasRecordGallons">Fuel Consumption(@(consumptionUnit))</label> <label for="gasRecordGallons">@($"{translator.Translate(userLanguage, "Fuel Consumption")}({consumptionUnit})")</label>
<input type="text" id="gasRecordGallons" class="form-control" placeholder="Amount of gas refueled" value="@(isNew ? "" : Model.GasRecord.Gallons)"> <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"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="gasIsFillToFull" checked="@Model.GasRecord.IsFillToFull"> <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>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="gasIsMissed" checked="@Model.GasRecord.MissedFuelUp"> <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> </div>
<label for="GasRecordCost">Cost</label> <label for="GasRecordCost">@translator.Translate(userLanguage,"Cost")</label>
@if (isNew) @if (isNew)
{ {
<div class="input-group"> <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"> <div class="input-group-text">
<select class="form-select form-select-sm" id="gasCostType"> <select class="form-select form-select-sm" id="gasCostType">
<option value="total">Total</option> <option value="total">@translator.Translate(userLanguage,"Total")</option>
<option value="unit">Unit</option> <option value="unit">@translator.Translate(userLanguage,"Unit")</option>
</select> </select>
</div> </div>
</div> </div>
} else } 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>
<div class="col-md-6 col-12"> <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> <textarea id="gasRecordNotes" class="form-control" rows="5">@Model.GasRecord.Notes</textarea>
@if (Model.GasRecord.Files.Any()) @if (Model.GasRecord.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.GasRecord.Files) @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"> <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>
} }
else 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"> <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>
</div> </div>
@@ -103,16 +113,16 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <script>

View File

@@ -1,20 +1,25 @@
@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 barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
var sortedByMPG = Model.OrderByDescending(x => x.Cost).ToList(); var sortedByMPG = Model.OrderByDescending(x => x.Cost).ToList();
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> <canvas id="bar-chart-mpg"></canvas>
<script> <script>
renderChart(); renderChart();
function renderChart() { function renderChart() {
var barGraphLabels = []; var barGraphLabels = [];
var barGraphData = []; var barGraphData = [];
//color gradient from high to low //color gradient from high to low
var barGraphColors = []; var barGraphColors = [];
var useDarkMode = getGlobalConfig().useDarkMode; var useDarkMode = getGlobalConfig().useDarkMode;
@foreach (CostForVehicleByMonth gasCost in Model) @foreach (CostForVehicleByMonth gasCost in Model)
{ {
@:barGraphLabels.push(decodeHTMLEntities("@gasCost.MonthName")); @:barGraphLabels.push(decodeHTMLEntities("@gasCost.MonthName"));
@@ -22,13 +27,13 @@
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName); var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
@:barGraphColors.push('@barGraphColors[index]'); @:barGraphColors.push('@barGraphColors[index]');
} }
new Chart($("#bar-chart-mpg"), { new Chart($("#bar-chart-mpg"), {
type: 'bar', type: 'bar',
data: { data: {
labels: barGraphLabels, labels: barGraphLabels,
datasets: [ datasets: [
{ {
label: "Fuel Mileage by Month", label: decodeHTMLEntities('@translator.Translate(userLanguage, "Fuel Mileage by Month")'),
backgroundColor: barGraphColors, backgroundColor: barGraphColors,
data: barGraphData data: barGraphData
} }
@@ -39,7 +44,7 @@
title: { title: {
display: true, display: true,
color: useDarkMode ? "#fff" : "#000", color: useDarkMode ? "#fff" : "#000",
text: 'Fuel Mileage by Month' text: decodeHTMLEntities('@translator.Translate(userLanguage, "Fuel Mileage by Month")')
}, },
legend: { legend: {
display: false, display: false,
@@ -65,9 +70,10 @@
}); });
} }
</script> </script>
} else }
else
{ {
<div class="text-center"> <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> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddNoteModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -14,17 +19,35 @@
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="noteIsPinned" checked="@Model.Pinned"> <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> </div>
<label for="noteDescription">Description</label> <label for="noteDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="noteDescription" class="form-control" placeholder="Description of the note" value="@(isNew ? "" : Model.Description)"> <input type="text" id="noteDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of the note")" value="@(isNew ? "" : Model.Description)">
</div> </div>
<div class="col-12"> <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> <textarea class="form-control vehicleNoteContainer" id="noteTextArea">@Model.NoteText</textarea>
</div> </div>
<div class="col-12"> <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"> <select multiple class="form-select" id="noteRecordTag">
@foreach (string tag in Model.Tags) @foreach (string tag in Model.Tags)
{ {
@@ -39,19 +62,27 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <script>
var uploadedFiles = [];
getUploadedFilesFromModel();
function getUploadedFilesFromModel() {
@foreach (UploadedFiles filesUploaded in Model.Files)
{
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
}
}
function getNoteModelData(){ function getNoteModelData(){
return { id: @Model.Id} 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 recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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) @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> <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> </datalist>
</div> </div>
<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> </div>
</div> </div>
@@ -32,8 +37,8 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-3">Description</th> <th scope="col" class="col-3">@translator.Translate(userLanguage,"Description")</th>
<th scope="col" class="col-9">Note</th> <th scope="col" class="col-9">@translator.Translate(userLanguage,"Note")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -47,7 +52,7 @@
{ {
<td class="col-3">@note.Description</td> <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> </tr>
} }
</tbody> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddOdometerRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,14 +17,14 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="odometerRecordDate">Date</label> <label for="odometerRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="odometerRecordMileage">Odometer</label> <label for="odometerRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="odometerRecordMileage" class="form-control" placeholder="Odometer reading" value="@(isNew ? "" : Model.Mileage)"> <input type="number" id="odometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading")" value="@(isNew ? "" : Model.Mileage)">
<label for="odometerRecordTag">Tags(optional)</label> <label for="odometerRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="odometerRecordTag"> <select multiple class="form-select" id="odometerRecordTag">
@foreach (string tag in Model.Tags) @foreach (string tag in Model.Tags)
{ {
@@ -28,22 +33,22 @@
</select> </select>
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="odometerRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else 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"> <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>
</div> </div>
@@ -53,16 +58,16 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <script>

View File

@@ -1,15 +1,18 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct(); var recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userLanguage = userConfig.UserLanguage;
} }
@model List<OdometerRecord> @model List<OdometerRecord>
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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) @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> <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) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('OdometerRecord')">@translator.Translate(userLanguage,"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="exportVehicleData('OdometerRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} }
else 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>
</div> </div>
@@ -54,9 +57,9 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th> <th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-3">Odometer</th> <th scope="col" class="col-3">@translator.Translate(userLanguage,"Odometer")</th>
<th scope="col" class="col-7 col-xl-8">Notes</th> <th scope="col" class="col-7 col-xl-8">@translator.Translate(userLanguage,"Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -64,7 +67,7 @@
{ {
<tr class="d-flex" style="cursor:pointer;" onclick="showEditOdometerRecordModal(@odometerRecord.Id)" data-tags='@string.Join(" ", odometerRecord.Tags)'> <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-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> <td class="col-7 col-xl-8 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td>
</tr> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddPlanRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,55 +17,57 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="planRecordDescription">Description</label> <label for="planRecordDescription">@translator.Translate(userLanguage, "Description")</label>
<input type="text" id="planRecordDescription" class="form-control" placeholder="Describe the Plan" value="@Model.Description"> <input type="text" id="planRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage, "Describe the Plan")" value="@Model.Description">
<label for="planRecordCost">Cost</label> <label for="planRecordCost">@translator.Translate(userLanguage, "Cost")</label>
<input type="text" id="planRecordCost" class="form-control" placeholder="Cost of the Plan" value="@Model.Cost"> <input type="text" id="planRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage, "Cost of the Plan")" value="@Model.Cost">
@if (isNew) @if (isNew)
{ {
@await Html.PartialAsync("_SupplyStore", "PlanRecord") @await Html.PartialAsync("_SupplyStore", "PlanRecord")
} }
<label for="planRecordType">Type</label> <label for="planRecordType">@translator.Translate(userLanguage, "Type")</label>
<select class="form-select" id="planRecordType"> <select class="form-select" id="planRecordType">
<!option value="ServiceRecord" @(Model.ImportMode == ImportMode.ServiceRecord || isNew ? "selected" : "")>Service</!option> <!option value="ServiceRecord" @(Model.ImportMode == ImportMode.ServiceRecord || isNew ? "selected" : "")>@translator.Translate(userLanguage, "Service")</!option>
<!option value="RepairRecord" @(Model.ImportMode == ImportMode.RepairRecord ? "selected" : "")>Repair</!option> <!option value="RepairRecord" @(Model.ImportMode == ImportMode.RepairRecord ? "selected" : "")>@translator.Translate(userLanguage, "Repair")</!option>
<!option value="UpgradeRecord" @(Model.ImportMode == ImportMode.UpgradeRecord ? "selected" : "")>Upgrade</!option> <!option value="UpgradeRecord" @(Model.ImportMode == ImportMode.UpgradeRecord ? "selected" : "")>@translator.Translate(userLanguage, "Upgrade")</!option>
</select> </select>
<label for="planRecordPriority">Priority</label> <label for="planRecordPriority">@translator.Translate(userLanguage, "Priority")</label>
<select class="form-select" id="planRecordPriority"> <select class="form-select" id="planRecordPriority">
<!option value="Critical" @(Model.Priority == PlanPriority.Critical ? "selected" : "")>Critical</!option> <!option value="Critical" @(Model.Priority == PlanPriority.Critical ? "selected" : "")>@translator.Translate(userLanguage, "Critical")</!option>
<!option value="Normal" @(Model.Priority == PlanPriority.Normal || isNew ? "selected" : "")>Normal</!option> <!option value="Normal" @(Model.Priority == PlanPriority.Normal || isNew ? "selected" : "")>@translator.Translate(userLanguage, "Normal")</!option>
<!option value="Low" @(Model.Priority == PlanPriority.Low ? "selected" : "")>Low</!option> <!option value="Low" @(Model.Priority == PlanPriority.Low ? "selected" : "")>@translator.Translate(userLanguage, "Low")</!option>
</select> </select>
<label for="planRecordProgress">Current Stage</label> <label for="planRecordProgress">@translator.Translate(userLanguage, "Current Stage")</label>
<select class="form-select" id="planRecordProgress"> <select class="form-select" id="planRecordProgress">
<!option value="Backlog" @(Model.Progress == PlanProgress.Backlog ||isNew ? "selected" : "")>Planned</!option> <!option value = "Backlog" @(Model.Progress == PlanProgress.Backlog || isNew ? "selected" : "")>@translator.Translate(userLanguage, "Planned")</!option>
<!option value="InProgress" @(Model.Progress == PlanProgress.InProgress ? "selected" : "")>Doing</!option> <!option value="InProgress" @(Model.Progress == PlanProgress.InProgress ? "selected" : "")>@translator.Translate(userLanguage, "Doing")</!option>
<!option value="Testing" @(Model.Progress == PlanProgress.Testing ? "selected" : "")>Testing</!option> <!option value = "Testing" @(Model.Progress == PlanProgress.Testing ? "selected" : "")>@translator.Translate(userLanguage, "Testing")</!option>
</select> </select>
@if (!isNew) @if (!isNew)
{ {
<label>Date Created: @Model.DateCreated</label> <label>@($"{translator.Translate(userLanguage, "Date Created")}: {Model.DateCreated}")</label>
<label>Last Modified: @Model.DateModified</label> <label>@($"{translator.Translate(userLanguage, "Last Modified")}: {Model.DateModified}")</label>
} }
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="planRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else 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"> <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>
</div> </div>
@@ -70,16 +77,16 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <script>
@@ -91,9 +98,9 @@
{ {
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" }); @:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
} }
} }
function getPlanRecordModelData() { function getPlanRecordModelData() {
return { return {
id: @Model.Id, id: @Model.Id,
dateCreated: decodeHTMLEntities('@(Model.DateCreated)') dateCreated: decodeHTMLEntities('@(Model.DateCreated)')
} }

View File

@@ -1,8 +1,11 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; 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 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 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); var testingItems = Model.Where(x => x.Progress == PlanProgress.Testing).OrderBy(x => x.Priority);
@@ -12,25 +15,25 @@
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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>
<div> <div>
@if (enableCsvImports) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('PlanRecord')">@translator.Translate(userLanguage,"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="exportVehicleData('PlanRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
</ul> </ul>
</div> </div>
} }
else 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>
</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="col-3 d-flex flex-column swimlane mid" ondragover="dragOver(event)" ondrop="dropBox(event, 'Backlog')">
<div class="row"> <div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;"> <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>
</div> </div>
@foreach (PlanRecord planRecord in backLogItems) @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="col-3 d-flex flex-column swimlane mid" ondragover="dragOver(event)" ondrop="dropBox(event, 'InProgress')">
<div class="row"> <div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;"> <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>
</div> </div>
@foreach (PlanRecord planRecord in inProgressItems) @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="col-3 d-flex flex-column swimlane" ondragover="dragOver(event)" ondrop="dropBox(event, 'Testing')">
<div class="row"> <div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;"> <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>
</div> </div>
@foreach (PlanRecord planRecord in testingItems) @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="col-3 d-flex flex-column swimlane end" ondragover="dragOver(event)" ondrop="dropBox(event, 'Done')">
<div class="row"> <div class="row">
<div class="col-12 d-flex justify-content-center" style="height:5vh;"> <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>
</div> </div>
@foreach (PlanRecord planRecord in doneItems) @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) @if (Model.UrgentCount + Model.VeryUrgentCount + Model.NotUrgentCount + Model.PastDueCount > 0)
{ {
<canvas id="donut-chart"></canvas> <canvas id="donut-chart"></canvas>
@@ -9,10 +16,15 @@
new Chart($("#donut-chart"), { new Chart($("#donut-chart"), {
type: 'doughnut', type: 'doughnut',
data: { 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: [ datasets: [
{ {
label: "Reminders by Category", label: decodeHTMLEntities('@translator.Translate(userLanguage, "Reminders by Category")'),
backgroundColor: ["#488f31", "#ffa600", "#de425b", "#cccccc"], backgroundColor: ["#488f31", "#ffa600", "#de425b", "#cccccc"],
data: [ data: [
@Model.NotUrgentCount, @Model.NotUrgentCount,
@@ -33,7 +45,7 @@
}, },
title: { title: {
display: true, display: true,
text: "Reminders by Urgency", text: decodeHTMLEntities('@translator.Translate(userLanguage, "Reminders by Urgency")'),
color: useDarkMode ? "#fff" : "#000" color: useDarkMode ? "#fff" : "#000"
}, },
} }
@@ -45,6 +57,6 @@
else else
{ {
<div class="text-center"> <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> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddReminderRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,42 +17,42 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12" id="reminderOptions"> <div class="col-md-6 col-12" id="reminderOptions">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="reminderDescription">Description</label> <label for="reminderDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="reminderDescription" class="form-control" placeholder="Reminder Description" value="@Model.Description"> <input type="text" id="reminderDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Reminder Description")" value="@Model.Description">
<label>Remind me on:</label> <label>@translator.Translate(userLanguage,"Remind me on")</label>
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricDate" value="@(ReminderMetric.Date)" checked="@(Model.Metric == ReminderMetric.Date)"> <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>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<div class="form-check form-check-inline"> <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)"> <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>
<div class="input-group"> <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"> <div class="input-group-text">
<button type="button" class="btn btn-sm btn-primary" onclick="appendMileageToOdometer(500)">+500</button> <button type="button" class="btn btn-sm btn-primary" onclick="appendMileageToOdometer(500)">+500</button>
</div> </div>
</div> </div>
<div class="form-check form-check-inline"> <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)"> <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> </div>
<div class="col-md-6 col-12"> <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> <textarea id="reminderNotes" class="form-control" rows="5">@Model.Notes</textarea>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" onChange="enableRecurring()" role="switch" id="reminderIsRecurring" checked="@Model.IsRecurring"> <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> </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")> <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="FiftyMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiftyMiles ? "selected" : "")>50 mi. / Km</!option>
<!option value="OneHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredMiles ? "selected" : "")>100 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> <!option value="FiveHundredMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiveHundredMiles ? "selected" : "")>500 mi. / Km</!option>
@@ -68,12 +73,12 @@
</select> </select>
<label for="reminderRecurringMonth">Month</label> <label for="reminderRecurringMonth">Month</label>
<select class="form-select" id="reminderRecurringMonth" @(Model.IsRecurring ? "" : "disabled")> <select class="form-select" id="reminderRecurringMonth" @(Model.IsRecurring ? "" : "disabled")>
<!option value="ThreeMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>3 Months</!option> <!option value="ThreeMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>@translator.Translate(userLanguage,"3 Months")</!option>
<!option value="SixMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>6 Months</!option> <!option value="SixMonths" @(Model.ReminderMonthInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>@translator.Translate(userLanguage,"6 Months")</!option>
<!option value="OneYear" @(Model.ReminderMonthInterval == ReminderMonthInterval.OneYear ? "selected" : "")>1 Year</!option> <!option value="OneYear" @(Model.ReminderMonthInterval == ReminderMonthInterval.OneYear ? "selected" : "")>@translator.Translate(userLanguage, "1 Year")</!option>
<!option value="TwoYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>2 Years</!option> <!option value="TwoYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>@translator.Translate(userLanguage, "2 Years")</!option>
<!option value="ThreeYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>3 Years</!option> <!option value="ThreeYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>@translator.Translate(userLanguage, "3 Years")</!option>
<!option value="FiveYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>5 Years</!option> <!option value="FiveYears" @(Model.ReminderMonthInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>@translator.Translate(userLanguage, "5 Years")</!option>
</select> </select>
</div> </div>
</div> </div>
@@ -83,16 +88,16 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <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(); var hasRefresh = Model.Where(x => (x.Urgency == ReminderUrgency.VeryUrgent || x.Urgency == ReminderUrgency.PastDue) && x.IsRecurring).Any();
} }
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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-success">@($"{translator.Translate(userLanguage, "# 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-secondary">@($"{translator.Translate(userLanguage, "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-danger">@($"{translator.Translate(userLanguage, "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-warning">@($"{translator.Translate(userLanguage, "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, "Not Urgent")}: {Model.Where(x => x.Urgency == ReminderUrgency.NotUrgent).Count()}")</span>
</div> </div>
<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> </div>
</div> </div>
@@ -26,15 +31,15 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-1">Urgency</th> <th scope="col" class="col-1">@translator.Translate(userLanguage, "Urgency")</th>
<th scope="col" class="col-2">Metric</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Metric")</th>
<th scope="col" class="@(hasRefresh ? "col-4" : "col-5")">Description</th> <th scope="col" class="@(hasRefresh ? "col-4" : "col-5")">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-3">Notes</th> <th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
@if (hasRefresh) @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> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -43,19 +48,19 @@
<tr class="d-flex" style="cursor:pointer;" onclick="showEditReminderRecordModal(@reminderRecord.Id)"> <tr class="d-flex" style="cursor:pointer;" onclick="showEditReminderRecordModal(@reminderRecord.Id)">
@if (reminderRecord.Urgency == ReminderUrgency.VeryUrgent) @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) 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) 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 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) @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="container reportTabContainer">
<div class="row hideOnPrint"> <div class="row hideOnPrint">
<div class="col-md-3 col-12 mt-2"> <div class="col-md-3 col-12 mt-2">
@@ -25,23 +32,23 @@
<div class="col-12 col-md-10 reportsCheckBoxContainer"> <div class="col-12 col-md-10 reportsCheckBoxContainer">
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="serviceExpenseCheck" value="1" checked> <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>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="repairExpenseCheck" value="2" checked> <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>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="upgradeExpenseCheck" value="3" checked> <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>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="gasExpenseCheck" value="4" checked> <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>
<div class="form-check form-check-inline"> <div class="form-check form-check-inline">
<input class="form-check-input" onChange="updateCheck()" type="checkbox" id="taxExpenseCheck" value="5" checked> <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> </div>
<div class="col-md-1 d-sm-none d-md-block"></div> <div class="col-md-1 d-sm-none d-md-block"></div>
@@ -56,10 +63,10 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<select class="form-select" onchange="updateReminderPie()" id="reminderOption"> <select class="form-select" onchange="updateReminderPie()" id="reminderOption">
<option value="0">As of Today</option> <option value="0">@translator.Translate(userLanguage, "As of Today")</option>
<option value="30">+30 Days</option> <option value="30">@translator.Translate(userLanguage, "+30 Days")</option>
<option value="60">+60 Days</option> <option value="60">@translator.Translate(userLanguage, "+60 Days")</option>
<option value="90">+90 Days</option> <option value="90">@translator.Translate(userLanguage, "+90 Days")</option>
</select> </select>
</div> </div>
</div> </div>
@@ -82,10 +89,10 @@
</div> </div>
<div class="col-md-3 col-12 chartContainer"> <div class="col-md-3 col-12 chartContainer">
<div class="d-grid"> <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>
<div class="d-grid"> <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> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddServiceRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,22 +17,22 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="serviceRecordDate">Date</label> <label for="serviceRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="serviceRecordMileage">Odometer</label> <label for="serviceRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="serviceRecordMileage" class="form-control" placeholder="Odometer reading when serviced" value="@(isNew ? "" : Model.Mileage)"> <input type="number" id="serviceRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when serviced")" value="@(isNew ? "" : Model.Mileage)">
<label for="serviceRecordDescription">Description</label> <label for="serviceRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="serviceRecordDescription" class="form-control" placeholder="Description of item(s) serviced(i.e. Oil Change)" value="@Model.Description"> <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">Cost</label> <label for="serviceRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="serviceRecordCost" class="form-control" placeholder="Cost of the service" value="@(isNew ? "" : Model.Cost)"> <input type="text" id="serviceRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of the service")" value="@(isNew ? "" : Model.Cost)">
@if (isNew) @if (isNew)
{ {
@await Html.PartialAsync("_SupplyStore", "ServiceRecord") @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"> <select multiple class="form-select" id="serviceRecordTag">
@foreach(string tag in Model.Tags) @foreach(string tag in Model.Tags)
{ {
@@ -36,15 +41,15 @@
</select> </select>
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="serviceRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else else
@@ -54,13 +59,13 @@
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck"> <input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck"> <label class="form-check-label" for="addReminderCheck">
Add Reminder @translator.Translate(userLanguage,"Add Reminder")
</label> </label>
</div> </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"> <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>
</div> </div>
@@ -71,25 +76,25 @@
@if (!isNew) @if (!isNew)
{ {
<div class="btn-group" style="margin-right:auto;"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><h6 class="dropdown-header">Move To</h6></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')">Repairs</a></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')">Upgrades</a></li> <li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'ServiceRecord', 'UpgradeRecord')">@translator.Translate(userLanguage,"Upgrades")</a></li>
</ul> </ul>
</div> </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) @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) 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> </div>
<script> <script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var userLanguage = userConfig.UserLanguage;
var recordTags = Model.SelectMany(x => x.Tags).Distinct(); var recordTags = Model.SelectMany(x => x.Tags).Distinct();
} }
@model List<ServiceRecord> @model List<ServiceRecord>
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# 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-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach(string recordTag in recordTags) @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> <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) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('ServiceRecord')">@translator.Translate(userLanguage,"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="exportVehicleData('ServiceRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} }
else 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>
</div> </div>
@@ -55,11 +58,11 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th> <th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-2">Odometer</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4">Description</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;">Cost</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">Notes</th> <th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -69,7 +72,7 @@
<td class="col-2 col-xl-1">@serviceRecord.Date.ToShortDateString()</td> <td class="col-2 col-xl-1">@serviceRecord.Date.ToShortDateString()</td>
<td class="col-2">@serviceRecord.Mileage</td> <td class="col-2">@serviceRecord.Mileage</td>
<td class="col-3 col-xl-4">@serviceRecord.Description</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> <td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(serviceRecord.Notes)</td>
</tr> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddSupplyRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,45 +17,45 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="supplyRecordDate">Date</label> <label for="supplyRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="supplyRecordPartNumber">Part Number</label> <label for="supplyRecordPartNumber">@translator.Translate(userLanguage,"Part Number")</label>
<input type="text" id="supplyRecordPartNumber" class="form-control" placeholder="Part #/Model #/SKU #" value="@(isNew ? "" : Model.PartNumber)"> <input type="text" id="supplyRecordPartNumber" class="form-control" placeholder="@translator.Translate(userLanguage,"Part #/Model #/SKU #")" value="@(isNew ? "" : Model.PartNumber)">
<label for="supplyRecordDescription">Description</label> <label for="supplyRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="supplyRecordDescription" class="form-control" placeholder="Description of the Part/Supplies" value="@Model.Description"> <input type="text" id="supplyRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of the Part/Supplies")" value="@Model.Description">
<label for="supplyRecordSupplier">Supplier/Vendor</label> <label for="supplyRecordSupplier">@translator.Translate(userLanguage,"Supplier/Vendor")</label>
<input type="text" id="supplyRecordSupplier" class="form-control" placeholder="Part Supplier" value="@Model.PartSupplier"> <input type="text" id="supplyRecordSupplier" class="form-control" placeholder="@translator.Translate(userLanguage,"Part Supplier")" value="@Model.PartSupplier">
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<label for="supplyRecordQuantity">Quantity</label> <label for="supplyRecordQuantity">@translator.Translate(userLanguage,"Quantity")</label>
<input type="text" id="supplyRecordQuantity" class="form-control" placeholder="Quantity" value="@(isNew ? "1" : Model.Quantity)"> <input type="text" id="supplyRecordQuantity" class="form-control" placeholder="@translator.Translate(userLanguage,"Quantity")" value="@(isNew ? "1" : Model.Quantity)">
</div> </div>
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<label for="supplyRecordCost">Cost</label> <label for="supplyRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="supplyRecordCost" class="form-control" placeholder="Cost" value="@(isNew ? "" : Model.Cost)"> <input type="text" id="supplyRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost")" value="@(isNew ? "" : Model.Cost)">
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="supplyRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else 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"> <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>
</div> </div>
@@ -60,16 +65,16 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <script>

View File

@@ -1,35 +1,38 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
} }
@model List<SupplyRecord> @model List<SupplyRecord>
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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-success">@($"{translator.Translate(userLanguage,"# 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-primary">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
</div> </div>
<div> <div>
@if (enableCsvImports) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('SupplyRecord')">@translator.Translate(userLanguage,"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="exportVehicleData('SupplyRecord')">@translator.Translate(userLanguage,"Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} }
else 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>
</div> </div>
@@ -44,13 +47,13 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th> <th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage,"Date")</th>
<th scope="col" class="col-2">Part #</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Part #")</th>
<th scope="col" class="col-2">Supplier</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Supplier")</th>
<th scope="col" class="col-2 col-xl-3">Description</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;">Quantity</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;">Cost</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">Notes</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <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="row">
<div class="col-12"> <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>
</div> </div>
<script> <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"> <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> <button type="button" class="btn-close" onclick="hideSuppliesModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -9,17 +16,16 @@
<div class="row"> <div class="row">
<div class="col-12" style="max-height:50vh; overflow-y:auto;"> <div class="col-12" style="max-height:50vh; overflow-y:auto;">
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
Supplies are requisitioned immediately after the record is created and cannot be modified. @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.")
If you have incorrectly entered the amount you needed you will need to correct it in the Supplies tab.
</div> </div>
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-1"></th> <th scope="col" class="col-1"></th>
<th scope="col" class="col-2">Quantity.</th> <th scope="col" class="col-2">@translator.Translate(userLanguage,"Quantity")</th>
<th scope="col" class="col-2">In Stock</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "In Stock")</th>
<th scope="col" class="col-5">Description</th> <th scope="col" class="col-5">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2">Unit Cost</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Unit Cost")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -43,7 +49,7 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="text-center"> <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> </div>
</div> </div>
@@ -51,8 +57,8 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<span id="supplySumLabel" style="margin-right:auto;">Total: 0.00</span> <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-secondary" onclick="hideSuppliesModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" class="btn btn-primary" disabled id="selectSuppliesButton" onclick="selectSupplies()">Select</button> <button type="button" class="btn btn-primary" disabled id="selectSuppliesButton" onclick="selectSupplies()">@translator.Translate(userLanguage, "Select")</button>
</div> </div>
<script> <script>
function recalculateTotal() { 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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddTaxRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,16 +17,16 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="taxRecordDate">Date</label> <label for="taxRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="taxRecordDescription">Description</label> <label for="taxRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="taxRecordDescription" class="form-control" placeholder="Description of tax paid(i.e. Registration)" value="@Model.Description"> <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">Cost</label> <label for="taxRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="taxRecordCost" class="form-control" placeholder="Cost of tax paid" value="@(isNew? "" : Model.Cost)"> <input type="text" id="taxRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of tax paid")" value="@(isNew? "" : Model.Cost)">
<label for="taxRecordTag">Tags(optional)</label> <label for="taxRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="taxRecordTag"> <select multiple class="form-select" id="taxRecordTag">
@foreach (string tag in Model.Tags) @foreach (string tag in Model.Tags)
{ {
@@ -30,29 +35,29 @@
</select> </select>
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="taxRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" onChange="enableTaxRecurring()" role="switch" id="taxIsRecurring" checked="@Model.IsRecurring"> <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> </div>
<label for="taxRecurringMonth">Month</label> <label for="taxRecurringMonth">@translator.Translate(userLanguage,"Month")</label>
<select class="form-select" id="taxRecurringMonth" @(Model.IsRecurring ? "" : "disabled")> <select class="form-select" id="taxRecurringMonth" @(Model.IsRecurring ? "" : "disabled")>
<!option value="OneMonth" @(Model.RecurringInterval == ReminderMonthInterval.OneMonth ? "selected" : "")>1 Month</!option> <!option value="OneMonth" @(Model.RecurringInterval == ReminderMonthInterval.OneMonth ? "selected" : "")>@translator.Translate(userLanguage,"1 Month")</!option>
<!option value="ThreeMonths" @(Model.RecurringInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>3 Months</!option> <!option value="ThreeMonths" @(Model.RecurringInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>@translator.Translate(userLanguage, "3 Months")</!option>
<!option value="SixMonths" @(Model.RecurringInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>6 Months</!option> <!option value="SixMonths" @(Model.RecurringInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>@translator.Translate(userLanguage, "6 Months")</!option>
<!option value="OneYear" @(Model.RecurringInterval == ReminderMonthInterval.OneYear ? "selected" : "")>1 Year</!option> <!option value="OneYear" @(Model.RecurringInterval == ReminderMonthInterval.OneYear ? "selected" : "")>@translator.Translate(userLanguage, "1 Year")</!option>
<!option value="TwoYears" @(Model.RecurringInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>2 Years</!option> <!option value="TwoYears" @(Model.RecurringInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>@translator.Translate(userLanguage, "2 Years")</!option>
<!option value="ThreeYears" @(Model.RecurringInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>3 Years</!option> <!option value="ThreeYears" @(Model.RecurringInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>@translator.Translate(userLanguage, "3 Years")</!option>
<!option value="FiveYears" @(Model.RecurringInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>5 Years</!option> <!option value="FiveYears" @(Model.RecurringInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>@translator.Translate(userLanguage, "5 Years")</!option>
</select> </select>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else else
@@ -62,13 +67,13 @@
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck"> <input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck"> <label class="form-check-label" for="addReminderCheck">
Add Reminder @translator.Translate(userLanguage,"Add Reminder")
</label> </label>
</div> </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"> <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>
</div> </div>
@@ -78,16 +83,16 @@
<div class="modal-footer"> <div class="modal-footer">
@if (!isNew) @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) @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) 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> </div>
<script> <script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct(); var recordTags = Model.SelectMany(x => x.Tags).Distinct();
} }
@model List<TaxRecord> @model List<TaxRecord>
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# 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-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags) @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> <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) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('TaxRecord')">@translator.Translate(userLanguage, "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="exportVehicleData('TaxRecord')">@translator.Translate(userLanguage, "Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} }
else 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>
</div> </div>
@@ -55,10 +58,10 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-3 col-xl-1">Date</th> <th scope="col" class="col-3 col-xl-1">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-4 col-xl-6">Description</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;">Cost</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">Notes</th> <th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -67,7 +70,7 @@
<tr class="d-flex" style="cursor:pointer;" onclick="showEditTaxRecordModal(@taxRecord.Id)" data-tags='@string.Join(" ", taxRecord.Tags)'> <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-3 col-xl-1">@taxRecord.Date.ToShortDateString()</td>
<td class="col-4 col-xl-6">@taxRecord.Description</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> <td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(taxRecord.Notes)</td>
</tr> </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 isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
} }
<div class="modal-header"> <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> <button type="button" class="btn-close" onclick="hideAddUpgradeRecordModal()" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@@ -12,22 +17,22 @@
<div class="row"> <div class="row">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;"> <input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="upgradeRecordDate">Date</label> <label for="upgradeRecordDate">@translator.Translate(userLanguage,"Date")</label>
<div class="input-group"> <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> <span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div> </div>
<label for="upgradeRecordMileage">Odometer</label> <label for="upgradeRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" id="upgradeRecordMileage" class="form-control" placeholder="Odometer reading when upgraded/modded" value="@(isNew ? "" : Model.Mileage)"> <input type="number" id="upgradeRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading when upgraded/modded")" value="@(isNew ? "" : Model.Mileage)">
<label for="upgradeRecordDescription">Description</label> <label for="upgradeRecordDescription">@translator.Translate(userLanguage,"Description")</label>
<input type="text" id="upgradeRecordDescription" class="form-control" placeholder="Description of item(s) upgraded/modded" value="@Model.Description"> <input type="text" id="upgradeRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Description of item(s) upgraded/modded")" value="@Model.Description">
<label for="upgradeRecordCost">Cost</label> <label for="upgradeRecordCost">@translator.Translate(userLanguage,"Cost")</label>
<input type="text" id="upgradeRecordCost" class="form-control" placeholder="Cost of the upgrade/mods" value="@(isNew ? "" : Model.Cost)"> <input type="text" id="upgradeRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"Cost of the upgrade/mods")" value="@(isNew ? "" : Model.Cost)">
@if (isNew) @if (isNew)
{ {
@await Html.PartialAsync("_SupplyStore", "UpgradeRecord") @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"> <select multiple class="form-select" id="upgradeRecordTag">
@foreach (string tag in Model.Tags) @foreach (string tag in Model.Tags)
{ {
@@ -36,15 +41,15 @@
</select> </select>
</div> </div>
<div class="col-md-6 col-12"> <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> <textarea id="upgradeRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any()) @if (Model.Files.Any())
{ {
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @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"> <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>
} }
else else
@@ -54,13 +59,13 @@
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck"> <input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
<label class="form-check-label" for="addReminderCheck"> <label class="form-check-label" for="addReminderCheck">
Add Reminder @translator.Translate(userLanguage,"Add Reminder")
</label> </label>
</div> </div>
} }
<label for="upgradeRecordFiles">Upload documents(optional)</label> <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"> <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>
</div> </div>
@@ -71,25 +76,25 @@
@if (!isNew) @if (!isNew)
{ {
<div class="btn-group" style="margin-right:auto;"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><h6 class="dropdown-header">Move To</h6></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')">Service Records</a></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')">Repairs</a></li> <li><a class="dropdown-item" href="#" onclick="moveRecord(@Model.Id, 'UpgradeRecord', 'RepairRecord')">@translator.Translate(userLanguage,"Repairs")</a></li>
</ul> </ul>
</div> </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) @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) 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> </div>
<script> <script>

View File

@@ -1,16 +1,19 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @inject IConfigHelper config
@inject ITranslationHelper translator
@{ @{
var enableCsvImports = config.GetUserConfig(User).EnableCsvImports; var userConfig = config.GetUserConfig(User);
var hideZero = config.GetUserConfig(User).HideZero; var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var recordTags = Model.SelectMany(x => x.Tags).Distinct(); var recordTags = Model.SelectMany(x => x.Tags).Distinct();
} }
@model List<UpgradeRecord> @model List<UpgradeRecord>
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap"> <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-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# 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-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage,"Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags) @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> <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) @if (enableCsvImports)
{ {
<div class="btn-group"> <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"> <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> <span class="visually-hidden">Toggle Dropdown</span>
</button> </button>
<ul class="dropdown-menu"> <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="showBulkImportModal('UpgradeRecord')">@translator.Translate(userLanguage, "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="exportVehicleData('UpgradeRecord')">@translator.Translate(userLanguage, "Export to CSV")</a></li>
<li><hr class="dropdown-divider"></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> </ul>
</div> </div>
} }
else 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>
</div> </div>
@@ -55,11 +58,11 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1">Date</th> <th scope="col" class="col-2 col-xl-1">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2">Odometer</th> <th scope="col" class="col-2">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4">Description</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;">Cost</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">Notes</th> <th scope="col" class="col-3">@translator.Translate(userLanguage, "Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -69,7 +72,7 @@
<td class="col-2 col-xl-1">@upgradeRecord.Date.ToShortDateString()</td> <td class="col-2 col-xl-1">@upgradeRecord.Date.ToShortDateString()</td>
<td class="col-2">@upgradeRecord.Mileage</td> <td class="col-2">@upgradeRecord.Mileage</td>
<td class="col-3 col-xl-4">@upgradeRecord.Description</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> <td class="col-3 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(upgradeRecord.Notes)</td>
</tr> </tr>
} }

View File

@@ -1,5 +1,12 @@
@model List<UploadedFiles> @using CarCareTracker.Helper
<label>Uploaded Documents</label> @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"> <ul class="list-group">
@foreach (UploadedFiles filesUploaded in Model) @foreach (UploadedFiles filesUploaded in Model)
{ {

View File

@@ -1,14 +1,17 @@
@using CarCareTracker.Helper @using CarCareTracker.Helper
@inject IConfigHelper config @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 @model VehicleHistoryViewModel
<div class="vehicleDetailTabContainer"> <div class="vehicleDetailTabContainer">
<div class="row mt-2"> <div class="row mt-2">
<div class="d-flex"> <div class="d-flex">
<img src="/defaults/lubelogger_logo.png" /> <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>
</div> </div>
<hr /> <hr />
@@ -24,21 +27,21 @@
<li class="list-group-item"> <li class="list-group-item">
@if (Model.VehicleData.IsElectric) @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 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> </li>
</ul> </ul>
</div> </div>
<div class="col-6"> <div class="col-6">
<ul class="list-group"> <ul class="list-group">
<li class="list-group-item">Last Reported Odometer Reading: @Model.Odometer</li> <li class="list-group-item">@($"{translator.Translate(userLanguage, "Last Reported Odometer Reading")}: {Model.Odometer}") </li>
<li class="list-group-item">Average Fuel Economy: @($"{Model.MPG}")</li> <li class="list-group-item">@($"{translator.Translate(userLanguage, "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">@($"{translator.Translate(userLanguage, "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, "Total Spent on Fuel")}: {Model.TotalGasCost.ToString("C")}") </li>
</ul> </ul>
</div> </div>
</div> </div>
@@ -48,12 +51,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 servicehistorytype">Type</th> <th scope="col" class="col-2 servicehistorytype">@translator.Translate(userLanguage, "Type")</th>
<th scope="col" class="col-1 servicehistorydate">Date</th> <th scope="col" class="col-1 servicehistorydate">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-1">Odometer</th> <th scope="col" class="col-1">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3">Description</th> <th scope="col" class="col-3">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-1">Cost</th> <th scope="col" class="col-1">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-4">Notes</th> <th scope="col" class="col-4">@translator.Translate(userLanguage, "Notes")</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -61,18 +64,21 @@
{ {
<tr class="d-flex"> <tr class="d-flex">
<td class="col-2 servicehistorytype"> <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> <span><i class="bi bi-card-checklist me-2"></i>@translator.Translate(userLanguage, "Service")</span>
} else if (reportData.DataType == ImportMode.RepairRecord) }
else if (reportData.DataType == ImportMode.RepairRecord)
{ {
<span><i class="bi bi-exclamation-octagon me-2"></i>Repair</span> <span><i class="bi bi-exclamation-octagon me-2"></i>@translator.Translate(userLanguage, "Repair")</span>
} else if (reportData.DataType == ImportMode.UpgradeRecord) }
else if (reportData.DataType == ImportMode.UpgradeRecord)
{ {
<span><i class="bi bi-wrench-adjustable me-2"></i>Upgrade</span> <span><i class="bi bi-wrench-adjustable me-2"></i>@translator.Translate(userLanguage, "Upgrade")</span>
} else if (reportData.DataType == ImportMode.TaxRecord) }
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>
<td class="col-1 servicehistorydate">@reportData.Date.ToShortDateString()</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; var isNew = Model.Id == 0;
if (Model.ImageLocation == "/defaults/noimage.png") if (Model.ImageLocation == "/defaults/noimage.png")
{ {
@@ -7,7 +12,7 @@
} }
} }
<div class="modal-header"> <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) @if (isNew)
{ {
<button type="button" class="btn-close" onclick="hideAddVehicleModal()" aria-label="Close"></button> <button type="button" class="btn-close" onclick="hideAddVehicleModal()" aria-label="Close"></button>
@@ -21,23 +26,23 @@
<div class="modal-body"> <div class="modal-body">
<form class="form-inline"> <form class="form-inline">
<div class="form-group"> <div class="form-group">
<label for="inputYear">Year</label> <label for="inputYear">@translator.Translate(userLanguage, "Year")</label>
<input type="number" id="inputYear" class="form-control" placeholder="Year(must be after 1900)" value="@(isNew ? "" : Model.Year)"> <input type="number" id="inputYear" class="form-control" placeholder="@translator.Translate(userLanguage, "Year(must be after 1900)")" value="@(isNew ? "" : Model.Year)">
<label for="inputMake">Make</label> <label for="inputMake">@translator.Translate(userLanguage, "Make")</label>
<input type="text" id="inputMake" class="form-control" placeholder="Make" value="@Model.Make"> <input type="text" id="inputMake" class="form-control" placeholder="@translator.Translate(userLanguage, "Make")" value="@Model.Make">
<label for="inputModel">Model</label> <label for="inputModel">@translator.Translate(userLanguage, "Model")</label>
<input type="text" id="inputModel" class="form-control" placeholder="Model" value="@Model.Model"> <input type="text" id="inputModel" class="form-control" placeholder="@translator.Translate(userLanguage, "Model")" value="@Model.Model">
<label for="inputLicensePlate">License Plate</label> <label for="inputLicensePlate">@translator.Translate(userLanguage, "License Plate")</label>
<input type="text" id="inputLicensePlate" class="form-control" placeholder="License Plate" value="@Model.LicensePlate"> <input type="text" id="inputLicensePlate" class="form-control" placeholder="@translator.Translate(userLanguage, "License Plate")" value="@Model.LicensePlate">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="inputIsElectric" checked="@Model.IsElectric"> <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>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="inputUseHours" checked="@Model.UseHours"> <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> </div>
<label for="inputTag">Tags(optional)</label> <label for="inputTag">@translator.Translate(userLanguage, "Tags(optional)")</label>
<select multiple class="form-select" id="inputTag"> <select multiple class="form-select" id="inputTag">
@foreach (string tag in Model.Tags) @foreach (string tag in Model.Tags)
{ {
@@ -46,11 +51,11 @@
</select> </select>
@if (!string.IsNullOrWhiteSpace(Model.ImageLocation)) @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"> <input onChange="uploadFileAsync(this)" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
} else } 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"> <input onChange="uploadFileAsync(this)" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
} }
</div> </div>
@@ -59,12 +64,12 @@
<div class="modal-footer"> <div class="modal-footer">
@if (isNew) @if (isNew)
{ {
<button type="button" class="btn btn-secondary" onclick="hideAddVehicleModal()">Cancel</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">Add New Vehicle</button> <button type="button" onclick="saveVehicle(false)" class="btn btn-primary">@translator.Translate(userLanguage, "Add New Vehicle")</button>
} else if (!isNew) } else if (!isNew)
{ {
<button type="button" class="btn btn-secondary" onclick="hideEditVehicleModal()">Cancel</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">Save Vehicle</button> <button type="button" onclick="saveVehicle(true)" class="btn btn-primary">@translator.Translate(userLanguage, "Save Vehicle")</button>
} }
</div> </div>
<script> <script>

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ services:
volumes: volumes:
- config:/App/config - config:/App/config
- data:/App/data - data:/App/data
- translations:/App/wwwroot/translations
- documents:/App/wwwroot/documents - documents:/App/wwwroot/documents
- images:/App/wwwroot/images - images:/App/wwwroot/images
- temp:/App/wwwroot/temp - temp:/App/wwwroot/temp
@@ -24,6 +25,7 @@ services:
volumes: volumes:
config: config:
data: data:
translations:
documents: documents:
images: images:
temp: 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; var vehicleId = GetVehicleId().vehicleId;
getVehicleCollisionRecords(vehicleId); getVehicleCollisionRecords(vehicleId);
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}); });
} else { } else {
@@ -72,7 +72,7 @@ function saveCollisionRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500); setTimeout(function () { showAddReminderModal(formValues); }, 500);
} }
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}) })
} }
@@ -94,7 +94,7 @@ function getAndValidateCollisionRecordValues() {
} else { } else {
$("#collisionRecordDate").removeClass("is-invalid"); $("#collisionRecordDate").removeClass("is-invalid");
} }
if (collisionMileage.trim() == '' || parseInt(collisionMileage) < 0) { if (collisionMileage.trim() == '' || isNaN(collisionMileage) || parseInt(collisionMileage) < 0) {
hasError = true; hasError = true;
$("#collisionRecordMileage").addClass("is-invalid"); $("#collisionRecordMileage").addClass("is-invalid");
} else { } else {

View File

@@ -4,6 +4,7 @@
$("#gasRecordModalContent").html(data); $("#gasRecordModalContent").html(data);
//initiate datepicker //initiate datepicker
initDatePicker($('#gasRecordDate')); initDatePicker($('#gasRecordDate'));
initTagSelector($("#gasRecordTag"));
$('#gasRecordModal').modal('show'); $('#gasRecordModal').modal('show');
} }
}); });
@@ -14,6 +15,7 @@ function showEditGasRecordModal(gasRecordId) {
$("#gasRecordModalContent").html(data); $("#gasRecordModalContent").html(data);
//initiate datepicker //initiate datepicker
initDatePicker($('#gasRecordDate')); initDatePicker($('#gasRecordDate'));
initTagSelector($("#gasRecordTag"));
$('#gasRecordModal').modal('show'); $('#gasRecordModal').modal('show');
$('#gasRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () { $('#gasRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
if (getGlobalConfig().useMarkDown) { if (getGlobalConfig().useMarkDown) {
@@ -43,7 +45,7 @@ function deleteGasRecord(gasRecordId) {
var vehicleId = GetVehicleId().vehicleId; var vehicleId = GetVehicleId().vehicleId;
getVehicleGasRecords(vehicleId); getVehicleGasRecords(vehicleId);
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}); });
} else { } else {
@@ -67,7 +69,7 @@ function saveGasRecordToVehicle(isEdit) {
saveScrollPosition(); saveScrollPosition();
getVehicleGasRecords(formValues.vehicleId); getVehicleGasRecords(formValues.vehicleId);
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}) })
} }
@@ -80,6 +82,7 @@ function getAndValidateGasRecordValues() {
var gasIsFillToFull = $("#gasIsFillToFull").is(":checked"); var gasIsFillToFull = $("#gasIsFillToFull").is(":checked");
var gasIsMissed = $("#gasIsMissed").is(":checked"); var gasIsMissed = $("#gasIsMissed").is(":checked");
var gasNotes = $("#gasRecordNotes").val(); var gasNotes = $("#gasRecordNotes").val();
var gasTags = $("#gasRecordTag").val();
var vehicleId = GetVehicleId().vehicleId; var vehicleId = GetVehicleId().vehicleId;
var gasRecordId = getGasRecordModelData().id; var gasRecordId = getGasRecordModelData().id;
//validation //validation
@@ -90,7 +93,7 @@ function getAndValidateGasRecordValues() {
} else { } else {
$("#gasRecordDate").removeClass("is-invalid"); $("#gasRecordDate").removeClass("is-invalid");
} }
if (gasMileage.trim() == '' || parseInt(gasMileage) < 0) { if (gasMileage.trim() == '' || isNaN(gasMileage) || parseInt(gasMileage) < 0) {
hasError = true; hasError = true;
$("#gasRecordMileage").addClass("is-invalid"); $("#gasRecordMileage").addClass("is-invalid");
} else { } else {
@@ -128,6 +131,7 @@ function getAndValidateGasRecordValues() {
gallons: gasGallons, gallons: gasGallons,
cost: gasCost, cost: gasCost,
files: uploadedFiles, files: uploadedFiles,
tags: gasTags,
isFillToFull: gasIsFillToFull, isFillToFull: gasIsFillToFull,
missedFuelUp: gasIsMissed, missedFuelUp: gasIsMissed,
notes: gasNotes notes: gasNotes
@@ -254,20 +258,23 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
elem.innerText = convertedAmount.toFixed(2); elem.innerText = convertedAmount.toFixed(2);
} }
//update labels up top. //update labels up top.
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().replace("Average Fuel Economy: ", "")); var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) { if (newAverage > 0) {
newAverage = 100 / newAverage; 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) { if (newMin > 0) {
newMin = 100 / newMin; 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) { if (newMax > 0) {
newMax = 100 / newMax; 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.text(sender.text().replace(sender.attr("data-unit"), "km/l"));
sender.attr("data-unit", "km/l"); sender.attr("data-unit", "km/l");
@@ -284,20 +291,23 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
convertedAmount = 100 / convertedAmount; convertedAmount = 100 / convertedAmount;
elem.innerText = convertedAmount.toFixed(2); 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) { if (newAverage > 0) {
newAverage = 100 / newAverage; 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) { if (newMin > 0) {
newMin = 100 / newMin; 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) { if (newMax > 0) {
newMax = 100 / newMax; 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.text(sender.text().replace(sender.attr("data-unit"), "l/100km"));
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) { function toggleUnits(sender) {
event.preventDefault(); event.preventDefault();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -92,7 +92,7 @@ function saveVehicle(isEdit) {
viewVehicle(vehicleId); viewVehicle(vehicleId);
} }
} else { } 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'); rowData.removeClass('override-hide');
$(sender).removeClass('bg-primary'); $(sender).removeClass('bg-primary');
$(sender).addClass('bg-secondary'); $(sender).addClass('bg-secondary');
updateAggregateLabels();
} else { } else {
rowData.addClass('override-hide'); rowData.addClass('override-hide');
$(`[data-tags~='${tagName}']`).removeClass('override-hide'); $(`[data-tags~='${tagName}']`).removeClass('override-hide');
updateAggregateLabels();
} }
} else { } else {
//hide table rows. //hide table rows.
rowData.addClass('override-hide'); rowData.addClass('override-hide');
$(`[data-tags~='${tagName}']`).removeClass('override-hide'); $(`[data-tags~='${tagName}']`).removeClass('override-hide');
updateAggregateLabels();
if ($(".tagfilter.bg-primary").length > 0) { if ($(".tagfilter.bg-primary").length > 0) {
//disabling other filters //disabling other filters
$(".tagfilter.bg-primary").addClass('bg-secondary'); $(".tagfilter.bg-primary").addClass('bg-secondary');
@@ -260,4 +263,17 @@ function filterTable(tabName, sender, isSort) {
$(sender).addClass('bg-primary'); $(sender).addClass('bg-primary');
$(sender).removeClass('bg-secondary'); $(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; var vehicleId = GetVehicleId().vehicleId;
getVehicleSupplyRecords(vehicleId); getVehicleSupplyRecords(vehicleId);
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}); });
} else { } else {
@@ -70,7 +70,7 @@ function saveSupplyRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500); setTimeout(function () { showAddReminderModal(formValues); }, 500);
} }
} else { } 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; var vehicleId = GetVehicleId().vehicleId;
getVehicleTaxRecords(vehicleId); getVehicleTaxRecords(vehicleId);
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}); });
} else { } else {
@@ -80,7 +80,7 @@ function saveTaxRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500); setTimeout(function () { showAddReminderModal(formValues); }, 500);
} }
} else { } 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; var vehicleId = GetVehicleId().vehicleId;
getVehicleUpgradeRecords(vehicleId); getVehicleUpgradeRecords(vehicleId);
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}); });
} else { } else {
@@ -72,7 +72,7 @@ function saveUpgradeRecordToVehicle(isEdit) {
setTimeout(function () { showAddReminderModal(formValues); }, 500); setTimeout(function () { showAddReminderModal(formValues); }, 500);
} }
} else { } else {
errorToast("An error has occurred, please try again later."); errorToast(genericErrorMessage());
} }
}) })
} }
@@ -94,7 +94,7 @@ function getAndValidateUpgradeRecordValues() {
} else { } else {
$("#upgradeRecordDate").removeClass("is-invalid"); $("#upgradeRecordDate").removeClass("is-invalid");
} }
if (upgradeMileage.trim() == '' || parseInt(upgradeMileage) < 0) { if (upgradeMileage.trim() == '' || isNaN(upgradeMileage) || parseInt(upgradeMileage) < 0) {
hasError = true; hasError = true;
$("#upgradeRecordMileage").addClass("is-invalid"); $("#upgradeRecordMileage").addClass("is-invalid");
} else { } else {

View File

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