Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f76991840 | ||
|
|
0487feb35e | ||
|
|
70308ed6eb | ||
|
|
bfdef5d296 | ||
|
|
a236c4a151 | ||
|
|
54d20b5573 | ||
|
|
ecd2b83cf0 | ||
|
|
80504e71c9 | ||
|
|
fb272c9c40 | ||
|
|
1c31477c37 | ||
|
|
2ecd286aa1 | ||
|
|
f28af456b3 | ||
|
|
c05b5e4c3d | ||
|
|
0f70f8212b | ||
|
|
f805e311b0 | ||
|
|
42f7bd298c | ||
|
|
e64d4f75b5 | ||
|
|
b972d5b7e5 | ||
|
|
f639c2c38b | ||
|
|
4e92155f5b | ||
|
|
78857c1b79 | ||
|
|
4e5d893850 | ||
|
|
a1fe446c2a | ||
|
|
f126a309f0 | ||
|
|
b2b129389a | ||
|
|
f2386fc9d8 | ||
|
|
d625a91ed7 | ||
|
|
b5f8d2d44e | ||
|
|
206b053d27 | ||
|
|
a2d16d7643 | ||
|
|
89345fd8eb | ||
|
|
4abf7fbba2 | ||
|
|
b5987927be | ||
|
|
bfef9b9498 | ||
|
|
6b4bbd8410 | ||
|
|
05f89073cd | ||
|
|
3776d6e11f | ||
|
|
2e301760f4 | ||
|
|
2817b5914f | ||
|
|
8276d8d720 | ||
|
|
711c7c14f7 | ||
|
|
f651f53ee6 | ||
|
|
c00ea88f73 | ||
|
|
583c83643a | ||
|
|
0775ede344 | ||
|
|
bf8ecdbe7a | ||
|
|
ed81d53175 | ||
|
|
4543e56c61 | ||
|
|
401724b66b | ||
|
|
b0773f5102 | ||
|
|
cff9289c39 | ||
|
|
69cfac1c6f | ||
|
|
0b05315671 | ||
|
|
af9c96c002 | ||
|
|
f1d4973f59 | ||
|
|
25db16c47f | ||
|
|
1286974fb6 | ||
|
|
997c045312 | ||
|
|
71c2e64daf | ||
|
|
1c2368f5a1 | ||
|
|
208eb22b8c | ||
|
|
25e32589d4 | ||
|
|
1727f0d193 | ||
|
|
0faffc2167 | ||
|
|
999649a095 | ||
|
|
cfb8e6ea55 |
19
.github/workflows/docker-image.yml
vendored
Normal file
19
.github/workflows/docker-image.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Docker Image CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Build and push the Docker image
|
||||||
|
run: |
|
||||||
|
docker login -u "hargata" -p "${{ secrets.GHCR_PAT }}" ghcr.io
|
||||||
|
docker build . --file Dockerfile --tag ghcr.io/hargata/lubelogger:latest
|
||||||
|
docker push ghcr.io/hargata/lubelogger:latest
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@ bin/
|
|||||||
obj/
|
obj/
|
||||||
wwwroot/images/
|
wwwroot/images/
|
||||||
cartracker.db
|
cartracker.db
|
||||||
|
data/cartracker.db
|
||||||
wwwroot/documents/
|
wwwroot/documents/
|
||||||
wwwroot/temp/
|
wwwroot/temp/
|
||||||
wwwroot/imports/
|
wwwroot/imports/
|
||||||
|
|||||||
@@ -53,8 +53,13 @@ namespace CarCareTracker.Controllers
|
|||||||
public IActionResult WriteToSettings(UserConfig userConfig)
|
public IActionResult WriteToSettings(UserConfig userConfig)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var configFileContents = System.IO.File.ReadAllText("userConfig.json");
|
if (!System.IO.File.Exists(StaticHelper.UserConfigPath))
|
||||||
|
{
|
||||||
|
//if file doesn't exist it might be because it's running on a mounted volume in docker.
|
||||||
|
System.IO.File.WriteAllText(StaticHelper.UserConfigPath, System.Text.Json.JsonSerializer.Serialize(new UserConfig()));
|
||||||
|
}
|
||||||
|
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath);
|
||||||
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
||||||
if (existingUserConfig is not null)
|
if (existingUserConfig is not null)
|
||||||
{
|
{
|
||||||
@@ -68,7 +73,7 @@ namespace CarCareTracker.Controllers
|
|||||||
userConfig.UserNameHash = string.Empty;
|
userConfig.UserNameHash = string.Empty;
|
||||||
userConfig.UserPasswordHash = string.Empty;
|
userConfig.UserPasswordHash = string.Empty;
|
||||||
}
|
}
|
||||||
System.IO.File.WriteAllText("userConfig.json", System.Text.Json.JsonSerializer.Serialize(userConfig));
|
System.IO.File.WriteAllText(StaticHelper.UserConfigPath, System.Text.Json.JsonSerializer.Serialize(userConfig));
|
||||||
return Json(true);
|
return Json(true);
|
||||||
} catch (Exception ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.Models;
|
using CarCareTracker.Helper;
|
||||||
|
using CarCareTracker.Models;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@@ -36,7 +37,7 @@ namespace CarCareTracker.Controllers
|
|||||||
//compare it against hashed credentials
|
//compare it against hashed credentials
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var configFileContents = System.IO.File.ReadAllText("userConfig.json");
|
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath);
|
||||||
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
||||||
if (existingUserConfig is not null)
|
if (existingUserConfig is not null)
|
||||||
{
|
{
|
||||||
@@ -74,7 +75,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var configFileContents = System.IO.File.ReadAllText("userConfig.json");
|
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath);
|
||||||
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
||||||
if (existingUserConfig is not null)
|
if (existingUserConfig is not null)
|
||||||
{
|
{
|
||||||
@@ -86,7 +87,7 @@ namespace CarCareTracker.Controllers
|
|||||||
existingUserConfig.UserNameHash = hashedUserName;
|
existingUserConfig.UserNameHash = hashedUserName;
|
||||||
existingUserConfig.UserPasswordHash = hashedPassword;
|
existingUserConfig.UserPasswordHash = hashedPassword;
|
||||||
}
|
}
|
||||||
System.IO.File.WriteAllText("userConfig.json", JsonSerializer.Serialize(existingUserConfig));
|
System.IO.File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
|
||||||
return Json(true);
|
return Json(true);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -101,7 +102,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var configFileContents = System.IO.File.ReadAllText("userConfig.json");
|
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath);
|
||||||
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
|
||||||
if (existingUserConfig is not null)
|
if (existingUserConfig is not null)
|
||||||
{
|
{
|
||||||
@@ -110,7 +111,7 @@ namespace CarCareTracker.Controllers
|
|||||||
existingUserConfig.UserNameHash = string.Empty;
|
existingUserConfig.UserNameHash = string.Empty;
|
||||||
existingUserConfig.UserPasswordHash = string.Empty;
|
existingUserConfig.UserPasswordHash = string.Empty;
|
||||||
}
|
}
|
||||||
System.IO.File.WriteAllText("userConfig.json", JsonSerializer.Serialize(existingUserConfig));
|
System.IO.File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
|
||||||
//destroy any login cookies.
|
//destroy any login cookies.
|
||||||
Response.Cookies.Delete("ACCESS_TOKEN");
|
Response.Cookies.Delete("ACCESS_TOKEN");
|
||||||
return Json(true);
|
return Json(true);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using CarCareTracker.Helper;
|
|||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using CarCareTracker.External.Implementations;
|
||||||
|
|
||||||
namespace CarCareTracker.Controllers
|
namespace CarCareTracker.Controllers
|
||||||
{
|
{
|
||||||
@@ -19,19 +20,21 @@ namespace CarCareTracker.Controllers
|
|||||||
private readonly IGasRecordDataAccess _gasRecordDataAccess;
|
private readonly IGasRecordDataAccess _gasRecordDataAccess;
|
||||||
private readonly ICollisionRecordDataAccess _collisionRecordDataAccess;
|
private readonly ICollisionRecordDataAccess _collisionRecordDataAccess;
|
||||||
private readonly ITaxRecordDataAccess _taxRecordDataAccess;
|
private readonly ITaxRecordDataAccess _taxRecordDataAccess;
|
||||||
|
private readonly IReminderRecordDataAccess _reminderRecordDataAccess;
|
||||||
private readonly IWebHostEnvironment _webEnv;
|
private readonly IWebHostEnvironment _webEnv;
|
||||||
private readonly bool _useDescending;
|
private readonly bool _useDescending;
|
||||||
private readonly IConfiguration _config;
|
private readonly IConfiguration _config;
|
||||||
private readonly IFileHelper _fileHelper;
|
private readonly IFileHelper _fileHelper;
|
||||||
|
|
||||||
public VehicleController(ILogger<VehicleController> logger,
|
public VehicleController(ILogger<VehicleController> logger,
|
||||||
IFileHelper fileHelper,
|
IFileHelper fileHelper,
|
||||||
IVehicleDataAccess dataAccess,
|
IVehicleDataAccess dataAccess,
|
||||||
INoteDataAccess noteDataAccess,
|
INoteDataAccess noteDataAccess,
|
||||||
IServiceRecordDataAccess serviceRecordDataAccess,
|
IServiceRecordDataAccess serviceRecordDataAccess,
|
||||||
IGasRecordDataAccess gasRecordDataAccess,
|
IGasRecordDataAccess gasRecordDataAccess,
|
||||||
ICollisionRecordDataAccess collisionRecordDataAccess,
|
ICollisionRecordDataAccess collisionRecordDataAccess,
|
||||||
ITaxRecordDataAccess taxRecordDataAccess,
|
ITaxRecordDataAccess taxRecordDataAccess,
|
||||||
|
IReminderRecordDataAccess reminderRecordDataAccess,
|
||||||
IWebHostEnvironment webEnv,
|
IWebHostEnvironment webEnv,
|
||||||
IConfiguration config)
|
IConfiguration config)
|
||||||
{
|
{
|
||||||
@@ -43,6 +46,7 @@ namespace CarCareTracker.Controllers
|
|||||||
_gasRecordDataAccess = gasRecordDataAccess;
|
_gasRecordDataAccess = gasRecordDataAccess;
|
||||||
_collisionRecordDataAccess = collisionRecordDataAccess;
|
_collisionRecordDataAccess = collisionRecordDataAccess;
|
||||||
_taxRecordDataAccess = taxRecordDataAccess;
|
_taxRecordDataAccess = taxRecordDataAccess;
|
||||||
|
_reminderRecordDataAccess = reminderRecordDataAccess;
|
||||||
_webEnv = webEnv;
|
_webEnv = webEnv;
|
||||||
_config = config;
|
_config = config;
|
||||||
_useDescending = bool.Parse(config[nameof(UserConfig.UseDescending)]);
|
_useDescending = bool.Parse(config[nameof(UserConfig.UseDescending)]);
|
||||||
@@ -90,6 +94,7 @@ namespace CarCareTracker.Controllers
|
|||||||
_collisionRecordDataAccess.DeleteAllCollisionRecordsByVehicleId(vehicleId) &&
|
_collisionRecordDataAccess.DeleteAllCollisionRecordsByVehicleId(vehicleId) &&
|
||||||
_taxRecordDataAccess.DeleteAllTaxRecordsByVehicleId(vehicleId) &&
|
_taxRecordDataAccess.DeleteAllTaxRecordsByVehicleId(vehicleId) &&
|
||||||
_noteDataAccess.DeleteNoteByVehicleId(vehicleId) &&
|
_noteDataAccess.DeleteNoteByVehicleId(vehicleId) &&
|
||||||
|
_reminderRecordDataAccess.DeleteAllReminderRecordsByVehicleId(vehicleId) &&
|
||||||
_dataAccess.DeleteVehicle(vehicleId);
|
_dataAccess.DeleteVehicle(vehicleId);
|
||||||
return Json(result);
|
return Json(result);
|
||||||
}
|
}
|
||||||
@@ -160,7 +165,8 @@ namespace CarCareTracker.Controllers
|
|||||||
_gasRecordDataAccess.SaveGasRecordToVehicle(convertedRecord);
|
_gasRecordDataAccess.SaveGasRecordToVehicle(convertedRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mode == "servicerecord")
|
}
|
||||||
|
else if (mode == "servicerecord")
|
||||||
{
|
{
|
||||||
var records = csv.GetRecords<ServiceRecordImport>().ToList();
|
var records = csv.GetRecords<ServiceRecordImport>().ToList();
|
||||||
if (records.Any())
|
if (records.Any())
|
||||||
@@ -179,7 +185,8 @@ namespace CarCareTracker.Controllers
|
|||||||
_serviceRecordDataAccess.SaveServiceRecordToVehicle(convertedRecord);
|
_serviceRecordDataAccess.SaveServiceRecordToVehicle(convertedRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mode == "repairrecord")
|
}
|
||||||
|
else if (mode == "repairrecord")
|
||||||
{
|
{
|
||||||
var records = csv.GetRecords<ServiceRecordImport>().ToList();
|
var records = csv.GetRecords<ServiceRecordImport>().ToList();
|
||||||
if (records.Any())
|
if (records.Any())
|
||||||
@@ -198,7 +205,8 @@ namespace CarCareTracker.Controllers
|
|||||||
_collisionRecordDataAccess.SaveCollisionRecordToVehicle(convertedRecord);
|
_collisionRecordDataAccess.SaveCollisionRecordToVehicle(convertedRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (mode == "taxrecord")
|
}
|
||||||
|
else if (mode == "taxrecord")
|
||||||
{
|
{
|
||||||
var records = csv.GetRecords<TaxRecordImport>().ToList();
|
var records = csv.GetRecords<TaxRecordImport>().ToList();
|
||||||
if (records.Any())
|
if (records.Any())
|
||||||
@@ -239,14 +247,16 @@ namespace CarCareTracker.Controllers
|
|||||||
bool useMPG = bool.Parse(_config[nameof(UserConfig.UseMPG)]);
|
bool useMPG = bool.Parse(_config[nameof(UserConfig.UseMPG)]);
|
||||||
var computedResults = new List<GasRecordViewModel>();
|
var computedResults = new List<GasRecordViewModel>();
|
||||||
int previousMileage = 0;
|
int previousMileage = 0;
|
||||||
|
decimal unFactoredConsumption = 0.00M;
|
||||||
|
int unFactoredMileage = 0;
|
||||||
//perform computation.
|
//perform computation.
|
||||||
for(int i = 0; i < result.Count; i++)
|
for (int i = 0; i < result.Count; i++)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
{
|
{
|
||||||
var currentObject = result[i];
|
var currentObject = result[i];
|
||||||
var deltaMileage = currentObject.Mileage - previousMileage;
|
var deltaMileage = currentObject.Mileage - previousMileage;
|
||||||
computedResults.Add(new GasRecordViewModel()
|
var gasRecordViewModel = new GasRecordViewModel()
|
||||||
{
|
{
|
||||||
Id = currentObject.Id,
|
Id = currentObject.Id,
|
||||||
VehicleId = currentObject.VehicleId,
|
VehicleId = currentObject.VehicleId,
|
||||||
@@ -255,10 +265,24 @@ namespace CarCareTracker.Controllers
|
|||||||
Gallons = currentObject.Gallons,
|
Gallons = currentObject.Gallons,
|
||||||
Cost = currentObject.Cost,
|
Cost = currentObject.Cost,
|
||||||
DeltaMileage = deltaMileage,
|
DeltaMileage = deltaMileage,
|
||||||
MilesPerGallon = useMPG ? (deltaMileage / currentObject.Gallons) : 100 / (deltaMileage / currentObject.Gallons),
|
|
||||||
CostPerGallon = (currentObject.Cost / currentObject.Gallons)
|
CostPerGallon = (currentObject.Cost / currentObject.Gallons)
|
||||||
});
|
};
|
||||||
} else
|
if (currentObject.IsFillToFull)
|
||||||
|
{
|
||||||
|
//if user filled to full.
|
||||||
|
gasRecordViewModel.MilesPerGallon = useMPG ? ((unFactoredMileage + deltaMileage) / (unFactoredConsumption + currentObject.Gallons)) : 100 / ((unFactoredMileage + deltaMileage) / (unFactoredConsumption + currentObject.Gallons));
|
||||||
|
//reset unFactored vars
|
||||||
|
unFactoredConsumption = 0;
|
||||||
|
unFactoredMileage = 0;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
unFactoredConsumption += currentObject.Gallons;
|
||||||
|
unFactoredMileage += deltaMileage;
|
||||||
|
gasRecordViewModel.MilesPerGallon = 0;
|
||||||
|
}
|
||||||
|
computedResults.Add(gasRecordViewModel);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
computedResults.Add(new GasRecordViewModel()
|
computedResults.Add(new GasRecordViewModel()
|
||||||
{
|
{
|
||||||
@@ -279,7 +303,13 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
computedResults = computedResults.OrderByDescending(x => DateTime.Parse(x.Date)).ThenByDescending(x => x.Mileage).ToList();
|
computedResults = computedResults.OrderByDescending(x => DateTime.Parse(x.Date)).ThenByDescending(x => x.Mileage).ToList();
|
||||||
}
|
}
|
||||||
return PartialView("_Gas", computedResults);
|
var vehicleIsElectric = _dataAccess.GetVehicleById(vehicleId).IsElectric;
|
||||||
|
var viewModel = new GasRecordViewModelContainer()
|
||||||
|
{
|
||||||
|
UseKwh = vehicleIsElectric,
|
||||||
|
GasRecords = computedResults
|
||||||
|
};
|
||||||
|
return PartialView("_Gas", viewModel);
|
||||||
}
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult SaveGasRecordToVehicleId(GasRecordInput gasRecord)
|
public IActionResult SaveGasRecordToVehicleId(GasRecordInput gasRecord)
|
||||||
@@ -291,7 +321,7 @@ namespace CarCareTracker.Controllers
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult GetAddGasRecordPartialView()
|
public IActionResult GetAddGasRecordPartialView()
|
||||||
{
|
{
|
||||||
return PartialView("_GasModal", new GasRecordInput());
|
return PartialView("_GasModal", new GasRecordInputContainer() { GasRecord = new GasRecordInput() });
|
||||||
}
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult GetGasRecordForEditById(int gasRecordId)
|
public IActionResult GetGasRecordForEditById(int gasRecordId)
|
||||||
@@ -305,9 +335,16 @@ namespace CarCareTracker.Controllers
|
|||||||
Cost = result.Cost,
|
Cost = result.Cost,
|
||||||
Date = result.Date.ToShortDateString(),
|
Date = result.Date.ToShortDateString(),
|
||||||
Files = result.Files,
|
Files = result.Files,
|
||||||
Gallons = result.Gallons
|
Gallons = result.Gallons,
|
||||||
|
IsFillToFull = result.IsFillToFull
|
||||||
};
|
};
|
||||||
return PartialView("_GasModal", convertedResult);
|
var vehicleIsElectric = _dataAccess.GetVehicleById(convertedResult.VehicleId).IsElectric;
|
||||||
|
var viewModel = new GasRecordInputContainer()
|
||||||
|
{
|
||||||
|
UseKwh = vehicleIsElectric,
|
||||||
|
GasRecord = convertedResult
|
||||||
|
};
|
||||||
|
return PartialView("_GasModal", viewModel);
|
||||||
}
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult DeleteGasRecordById(int gasRecordId)
|
public IActionResult DeleteGasRecordById(int gasRecordId)
|
||||||
@@ -349,9 +386,11 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
var result = _serviceRecordDataAccess.GetServiceRecordById(serviceRecordId);
|
var result = _serviceRecordDataAccess.GetServiceRecordById(serviceRecordId);
|
||||||
//convert to Input object.
|
//convert to Input object.
|
||||||
var convertedResult = new ServiceRecordInput { Id = result.Id,
|
var convertedResult = new ServiceRecordInput
|
||||||
Cost = result.Cost,
|
{
|
||||||
Date = result.Date.ToShortDateString(),
|
Id = result.Id,
|
||||||
|
Cost = result.Cost,
|
||||||
|
Date = result.Date.ToShortDateString(),
|
||||||
Description = result.Description,
|
Description = result.Description,
|
||||||
Mileage = result.Mileage,
|
Mileage = result.Mileage,
|
||||||
Notes = result.Notes,
|
Notes = result.Notes,
|
||||||
@@ -360,7 +399,7 @@ namespace CarCareTracker.Controllers
|
|||||||
};
|
};
|
||||||
return PartialView("_ServiceRecordModal", convertedResult);
|
return PartialView("_ServiceRecordModal", convertedResult);
|
||||||
}
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult DeleteServiceRecordById(int serviceRecordId)
|
public IActionResult DeleteServiceRecordById(int serviceRecordId)
|
||||||
{
|
{
|
||||||
var result = _serviceRecordDataAccess.DeleteServiceRecordById(serviceRecordId);
|
var result = _serviceRecordDataAccess.DeleteServiceRecordById(serviceRecordId);
|
||||||
@@ -508,12 +547,178 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
gasRecords.RemoveAll(x => x.Date.Year != year);
|
gasRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
}
|
}
|
||||||
var groupedGasRecord = gasRecords.GroupBy(x => x.Date.Month).OrderBy(x=>x.Key).Select(x => new GasCostForVehicleByMonth {
|
var groupedGasRecord = gasRecords.GroupBy(x => x.Date.Month).OrderBy(x => x.Key).Select(x => new GasCostForVehicleByMonth
|
||||||
|
{
|
||||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||||
Cost = x.Sum(y=>y.Cost)
|
Cost = x.Sum(y => y.Cost)
|
||||||
}).ToList();
|
}).ToList();
|
||||||
return PartialView("_GasCostByMonthReport", groupedGasRecord);
|
return PartialView("_GasCostByMonthReport", groupedGasRecord);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
#region "Reminders"
|
||||||
|
private int GetMaxMileage(int vehicleId)
|
||||||
|
{
|
||||||
|
var numbersArray = new List<int>();
|
||||||
|
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
|
||||||
|
if (serviceRecords.Any())
|
||||||
|
{
|
||||||
|
numbersArray.Add(serviceRecords.Max(x => x.Mileage));
|
||||||
|
}
|
||||||
|
var repairRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId);
|
||||||
|
if (repairRecords.Any())
|
||||||
|
{
|
||||||
|
numbersArray.Add(repairRecords.Max(x => x.Mileage));
|
||||||
|
}
|
||||||
|
var gasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
||||||
|
if (gasRecords.Any())
|
||||||
|
{
|
||||||
|
numbersArray.Add(gasRecords.Max(x => x.Mileage));
|
||||||
|
}
|
||||||
|
return numbersArray.Any() ? numbersArray.Max() : 0;
|
||||||
|
}
|
||||||
|
private List<ReminderRecordViewModel> GetRemindersAndUrgency(int vehicleId)
|
||||||
|
{
|
||||||
|
var currentMileage = GetMaxMileage(vehicleId);
|
||||||
|
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicleId);
|
||||||
|
List<ReminderRecordViewModel> reminderViewModels = new List<ReminderRecordViewModel>();
|
||||||
|
foreach(var reminder in reminders)
|
||||||
|
{
|
||||||
|
var reminderViewModel = new ReminderRecordViewModel()
|
||||||
|
{
|
||||||
|
Id = reminder.Id,
|
||||||
|
VehicleId = reminder.VehicleId,
|
||||||
|
Date = reminder.Date,
|
||||||
|
Mileage = reminder.Mileage,
|
||||||
|
Description = reminder.Description,
|
||||||
|
Notes = reminder.Notes,
|
||||||
|
Metric = reminder.Metric
|
||||||
|
};
|
||||||
|
if (reminder.Metric == ReminderMetric.Both)
|
||||||
|
{
|
||||||
|
if (reminder.Date < DateTime.Now)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Date;
|
||||||
|
}
|
||||||
|
else if (reminder.Mileage < currentMileage)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||||
|
}
|
||||||
|
else if (reminder.Date < DateTime.Now.AddDays(7))
|
||||||
|
{
|
||||||
|
//if less than a week from today or less than 50 miles from current mileage then very urgent.
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||||
|
//have to specify by which metric this reminder is urgent.
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Date;
|
||||||
|
}
|
||||||
|
else if (reminder.Mileage < currentMileage + 50)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||||
|
}
|
||||||
|
else if (reminder.Date < DateTime.Now.AddDays(30))
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Date;
|
||||||
|
}
|
||||||
|
else if (reminder.Mileage < currentMileage + 100)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||||
|
}
|
||||||
|
} else if (reminder.Metric == ReminderMetric.Date)
|
||||||
|
{
|
||||||
|
if (reminder.Date < DateTime.Now)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||||
|
}
|
||||||
|
else if (reminder.Date < DateTime.Now.AddDays(7))
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||||
|
}
|
||||||
|
else if (reminder.Date < DateTime.Now.AddDays(30))
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||||
|
}
|
||||||
|
} else if (reminder.Metric == ReminderMetric.Odometer)
|
||||||
|
{
|
||||||
|
if (reminder.Mileage < currentMileage)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.PastDue;
|
||||||
|
reminderViewModel.Metric = ReminderMetric.Odometer;
|
||||||
|
}
|
||||||
|
else if (reminder.Mileage < currentMileage + 50)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
|
||||||
|
}
|
||||||
|
else if (reminder.Mileage < currentMileage + 100)
|
||||||
|
{
|
||||||
|
reminderViewModel.Urgency = ReminderUrgency.Urgent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reminderViewModels.Add(reminderViewModel);
|
||||||
|
}
|
||||||
|
return reminderViewModels;
|
||||||
|
}
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult GetVehicleHaveUrgentOrPastDueReminders(int vehicleId)
|
||||||
|
{
|
||||||
|
var result = GetRemindersAndUrgency(vehicleId);
|
||||||
|
if (result.Where(x=>x.Urgency == ReminderUrgency.VeryUrgent || x.Urgency == ReminderUrgency.PastDue).Any())
|
||||||
|
{
|
||||||
|
return Json(true);
|
||||||
|
}
|
||||||
|
return Json(false);
|
||||||
|
}
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult GetReminderRecordsByVehicleId(int vehicleId)
|
||||||
|
{
|
||||||
|
var result = GetRemindersAndUrgency(vehicleId);
|
||||||
|
result = result.OrderByDescending(x=>x.Urgency).ToList();
|
||||||
|
return PartialView("_ReminderRecords", result);
|
||||||
|
}
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult SaveReminderRecordToVehicleId(ReminderRecordInput reminderRecord)
|
||||||
|
{
|
||||||
|
var result = _reminderRecordDataAccess.SaveReminderRecordToVehicle(reminderRecord.ToReminderRecord());
|
||||||
|
return Json(result);
|
||||||
|
}
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult GetAddReminderRecordPartialView(ReminderRecordInput? reminderModel)
|
||||||
|
{
|
||||||
|
if (reminderModel is not null)
|
||||||
|
{
|
||||||
|
return PartialView("_ReminderRecordModal", reminderModel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return PartialView("_ReminderRecordModal", new ReminderRecordInput());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult GetReminderRecordForEditById(int reminderRecordId)
|
||||||
|
{
|
||||||
|
var result = _reminderRecordDataAccess.GetReminderRecordById(reminderRecordId);
|
||||||
|
//convert to Input object.
|
||||||
|
var convertedResult = new ReminderRecordInput
|
||||||
|
{
|
||||||
|
Id = result.Id,
|
||||||
|
Date = result.Date.ToShortDateString(),
|
||||||
|
Description = result.Description,
|
||||||
|
Notes = result.Notes,
|
||||||
|
VehicleId = result.VehicleId,
|
||||||
|
Mileage = result.Mileage,
|
||||||
|
Metric = result.Metric
|
||||||
|
};
|
||||||
|
return PartialView("_ReminderRecordModal", convertedResult);
|
||||||
|
}
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult DeleteReminderRecordById(int reminderRecordId)
|
||||||
|
{
|
||||||
|
var result = _reminderRecordDataAccess.DeleteReminderRecordById(reminderRecordId);
|
||||||
|
return Json(result);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
|
||||||
|
WORKDIR /App
|
||||||
|
|
||||||
|
COPY . ./
|
||||||
|
RUN dotnet restore
|
||||||
|
RUN dotnet publish -c Release -o out
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
||||||
|
WORKDIR /App
|
||||||
|
COPY --from=build-env /App/out .
|
||||||
|
EXPOSE 8080
|
||||||
|
CMD ["./CarCareTracker"]
|
||||||
9
Enum/ReminderMetric.cs
Normal file
9
Enum/ReminderMetric.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public enum ReminderMetric
|
||||||
|
{
|
||||||
|
Date = 0,
|
||||||
|
Odometer = 1,
|
||||||
|
Both = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Enum/ReminderUrgency.cs
Normal file
10
Enum/ReminderUrgency.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public enum ReminderUrgency
|
||||||
|
{
|
||||||
|
NotUrgent = 0,
|
||||||
|
Urgent = 1,
|
||||||
|
VeryUrgent = 2,
|
||||||
|
PastDue = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.External.Interfaces;
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace CarCareTracker.External.Implementations
|
|||||||
{
|
{
|
||||||
public class CollisionRecordDataAccess : ICollisionRecordDataAccess
|
public class CollisionRecordDataAccess : ICollisionRecordDataAccess
|
||||||
{
|
{
|
||||||
private static string dbName = "cartracker.db";
|
private static string dbName = StaticHelper.DbName;
|
||||||
private static string tableName = "collisionrecords";
|
private static string tableName = "collisionrecords";
|
||||||
public List<CollisionRecord> GetCollisionRecordsByVehicleId(int vehicleId)
|
public List<CollisionRecord> GetCollisionRecordsByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.External.Interfaces;
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace CarCareTracker.External.Implementations
|
|||||||
{
|
{
|
||||||
public class GasRecordDataAccess: IGasRecordDataAccess
|
public class GasRecordDataAccess: IGasRecordDataAccess
|
||||||
{
|
{
|
||||||
private static string dbName = "cartracker.db";
|
private static string dbName = StaticHelper.DbName;
|
||||||
private static string tableName = "gasrecords";
|
private static string tableName = "gasrecords";
|
||||||
public List<GasRecord> GetGasRecordsByVehicleId(int vehicleId)
|
public List<GasRecord> GetGasRecordsByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
|
|||||||
3
External/Implementations/NoteDataAccess.cs
vendored
3
External/Implementations/NoteDataAccess.cs
vendored
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.External.Interfaces;
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace CarCareTracker.External.Implementations
|
|||||||
{
|
{
|
||||||
public class NoteDataAccess: INoteDataAccess
|
public class NoteDataAccess: INoteDataAccess
|
||||||
{
|
{
|
||||||
private static string dbName = "cartracker.db";
|
private static string dbName = StaticHelper.DbName;
|
||||||
private static string tableName = "notes";
|
private static string tableName = "notes";
|
||||||
public Note GetNoteByVehicleId(int vehicleId)
|
public Note GetNoteByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
|
|||||||
57
External/Implementations/ReminderRecordDataAccess.cs
vendored
Normal file
57
External/Implementations/ReminderRecordDataAccess.cs
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
|
using CarCareTracker.Models;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
|
namespace CarCareTracker.External.Implementations
|
||||||
|
{
|
||||||
|
public class ReminderRecordDataAccess : IReminderRecordDataAccess
|
||||||
|
{
|
||||||
|
private static string dbName = StaticHelper.DbName;
|
||||||
|
private static string tableName = "reminderrecords";
|
||||||
|
public List<ReminderRecord> GetReminderRecordsByVehicleId(int vehicleId)
|
||||||
|
{
|
||||||
|
using (var db = new LiteDatabase(dbName))
|
||||||
|
{
|
||||||
|
var table = db.GetCollection<ReminderRecord>(tableName);
|
||||||
|
var reminderRecords = table.Find(Query.EQ(nameof(ReminderRecord.VehicleId), vehicleId));
|
||||||
|
return reminderRecords.ToList() ?? new List<ReminderRecord>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public ReminderRecord GetReminderRecordById(int reminderRecordId)
|
||||||
|
{
|
||||||
|
using (var db = new LiteDatabase(dbName))
|
||||||
|
{
|
||||||
|
var table = db.GetCollection<ReminderRecord>(tableName);
|
||||||
|
return table.FindById(reminderRecordId);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public bool DeleteReminderRecordById(int reminderRecordId)
|
||||||
|
{
|
||||||
|
using (var db = new LiteDatabase(dbName))
|
||||||
|
{
|
||||||
|
var table = db.GetCollection<ReminderRecord>(tableName);
|
||||||
|
table.Delete(reminderRecordId);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public bool SaveReminderRecordToVehicle(ReminderRecord reminderRecord)
|
||||||
|
{
|
||||||
|
using (var db = new LiteDatabase(dbName))
|
||||||
|
{
|
||||||
|
var table = db.GetCollection<ReminderRecord>(tableName);
|
||||||
|
table.Upsert(reminderRecord);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public bool DeleteAllReminderRecordsByVehicleId(int vehicleId)
|
||||||
|
{
|
||||||
|
using (var db = new LiteDatabase(dbName))
|
||||||
|
{
|
||||||
|
var table = db.GetCollection<ReminderRecord>(tableName);
|
||||||
|
var reminderRecords = table.DeleteMany(Query.EQ(nameof(ReminderRecord.VehicleId), vehicleId));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.External.Interfaces;
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace CarCareTracker.External.Implementations
|
|||||||
{
|
{
|
||||||
public class ServiceRecordDataAccess: IServiceRecordDataAccess
|
public class ServiceRecordDataAccess: IServiceRecordDataAccess
|
||||||
{
|
{
|
||||||
private static string dbName = "cartracker.db";
|
private static string dbName = StaticHelper.DbName;
|
||||||
private static string tableName = "servicerecords";
|
private static string tableName = "servicerecords";
|
||||||
public List<ServiceRecord> GetServiceRecordsByVehicleId(int vehicleId)
|
public List<ServiceRecord> GetServiceRecordsByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.External.Interfaces;
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace CarCareTracker.External.Implementations
|
|||||||
{
|
{
|
||||||
public class TaxRecordDataAccess : ITaxRecordDataAccess
|
public class TaxRecordDataAccess : ITaxRecordDataAccess
|
||||||
{
|
{
|
||||||
private static string dbName = "cartracker.db";
|
private static string dbName = StaticHelper.DbName;
|
||||||
private static string tableName = "taxrecords";
|
private static string tableName = "taxrecords";
|
||||||
public List<TaxRecord> GetTaxRecordsByVehicleId(int vehicleId)
|
public List<TaxRecord> GetTaxRecordsByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.External.Interfaces;
|
using CarCareTracker.External.Interfaces;
|
||||||
|
using CarCareTracker.Helper;
|
||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
|
|
||||||
@@ -6,7 +7,7 @@ namespace CarCareTracker.External.Implementations
|
|||||||
{
|
{
|
||||||
public class VehicleDataAccess: IVehicleDataAccess
|
public class VehicleDataAccess: IVehicleDataAccess
|
||||||
{
|
{
|
||||||
private static string dbName = "cartracker.db";
|
private static string dbName = StaticHelper.DbName;
|
||||||
private static string tableName = "vehicles";
|
private static string tableName = "vehicles";
|
||||||
public bool SaveVehicle(Vehicle vehicle)
|
public bool SaveVehicle(Vehicle vehicle)
|
||||||
{
|
{
|
||||||
|
|||||||
13
External/Interfaces/IReminderRecordDataAccess.cs
vendored
Normal file
13
External/Interfaces/IReminderRecordDataAccess.cs
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using CarCareTracker.Models;
|
||||||
|
|
||||||
|
namespace CarCareTracker.External.Interfaces
|
||||||
|
{
|
||||||
|
public interface IReminderRecordDataAccess
|
||||||
|
{
|
||||||
|
public List<ReminderRecord> GetReminderRecordsByVehicleId(int vehicleId);
|
||||||
|
public ReminderRecord GetReminderRecordById(int reminderRecordId);
|
||||||
|
public bool DeleteReminderRecordById(int reminderRecordId);
|
||||||
|
public bool SaveReminderRecordToVehicle(ReminderRecord reminderRecord);
|
||||||
|
public bool DeleteAllReminderRecordsByVehicleId(int vehicleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Helper/StaticHelper.cs
Normal file
11
Helper/StaticHelper.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace CarCareTracker.Helper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// helper method for static vars
|
||||||
|
/// </summary>
|
||||||
|
public static class StaticHelper
|
||||||
|
{
|
||||||
|
public static string DbName = "data/cartracker.db";
|
||||||
|
public static string UserConfigPath = "config/userConfig.json";
|
||||||
|
}
|
||||||
|
}
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 ivancheahhh
|
Copyright (c) 2023 Hargata Softworks
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Gallons { get; set; }
|
public decimal Gallons { get; set; }
|
||||||
public decimal Cost { get; set; }
|
public decimal Cost { get; set; }
|
||||||
|
public bool IsFillToFull { get; set; } = true;
|
||||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,17 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public decimal Gallons { get; set; }
|
public decimal Gallons { get; set; }
|
||||||
public decimal Cost { get; set; }
|
public decimal Cost { get; set; }
|
||||||
|
public bool IsFillToFull { get; set; } = true;
|
||||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||||
public GasRecord ToGasRecord() { return new GasRecord { Id = Id, Cost = Cost, Date = DateTime.Parse(Date), Gallons = Gallons, Mileage = Mileage, VehicleId = VehicleId, Files = Files }; }
|
public GasRecord ToGasRecord() { return new GasRecord {
|
||||||
|
Id = Id,
|
||||||
|
Cost = Cost,
|
||||||
|
Date = DateTime.Parse(Date),
|
||||||
|
Gallons = Gallons,
|
||||||
|
Mileage = Mileage,
|
||||||
|
VehicleId = VehicleId,
|
||||||
|
Files = Files,
|
||||||
|
IsFillToFull = IsFillToFull
|
||||||
|
}; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
Models/GasRecord/GasRecordInputContainer.cs
Normal file
8
Models/GasRecord/GasRecordInputContainer.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class GasRecordInputContainer
|
||||||
|
{
|
||||||
|
public bool UseKwh { get; set; }
|
||||||
|
public GasRecordInput GasRecord { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Models/GasRecord/GasRecordViewModelContainer.cs
Normal file
8
Models/GasRecord/GasRecordViewModelContainer.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class GasRecordViewModelContainer
|
||||||
|
{
|
||||||
|
public bool UseKwh { get; set; }
|
||||||
|
public List<GasRecordViewModel> GasRecords { get; set; } = new List<GasRecordViewModel>();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Models/Reminder/ReminderRecord.cs
Normal file
13
Models/Reminder/ReminderRecord.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class ReminderRecord
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int VehicleId { get; set; }
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
public int Mileage { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
public ReminderMetric Metric { get; set; } = ReminderMetric.Date;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Models/Reminder/ReminderRecordInput.cs
Normal file
21
Models/Reminder/ReminderRecordInput.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class ReminderRecordInput
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int VehicleId { get; set; }
|
||||||
|
public string Date { get; set; } = DateTime.Now.AddDays(1).ToShortDateString();
|
||||||
|
public int Mileage { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
public ReminderMetric Metric { get; set; } = ReminderMetric.Date;
|
||||||
|
public ReminderRecord ToReminderRecord() { return new ReminderRecord {
|
||||||
|
Id = Id,
|
||||||
|
VehicleId = VehicleId,
|
||||||
|
Date = DateTime.Parse(string.IsNullOrWhiteSpace(Date) ? DateTime.Now.AddDays(1).ToShortDateString() : Date),
|
||||||
|
Mileage = Mileage,
|
||||||
|
Description = Description,
|
||||||
|
Metric = Metric,
|
||||||
|
Notes = Notes }; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Models/Reminder/ReminderRecordViewModel.cs
Normal file
17
Models/Reminder/ReminderRecordViewModel.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class ReminderRecordViewModel
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int VehicleId { get; set; }
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
public int Mileage { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public string Notes { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Reason why this reminder is urgent
|
||||||
|
/// </summary>
|
||||||
|
public ReminderMetric Metric { get; set; } = ReminderMetric.Date;
|
||||||
|
public ReminderUrgency Urgency { get; set; } = ReminderUrgency.NotUrgent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,5 +8,6 @@
|
|||||||
public string Make { get; set; }
|
public string Make { get; set; }
|
||||||
public string Model { get; set; }
|
public string Model { get; set; }
|
||||||
public string LicensePlate { get; set; }
|
public string LicensePlate { get; set; }
|
||||||
|
public bool IsElectric { get; set; } = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,10 +15,16 @@ builder.Services.AddSingleton<IServiceRecordDataAccess, ServiceRecordDataAccess>
|
|||||||
builder.Services.AddSingleton<IGasRecordDataAccess, GasRecordDataAccess>();
|
builder.Services.AddSingleton<IGasRecordDataAccess, GasRecordDataAccess>();
|
||||||
builder.Services.AddSingleton<ICollisionRecordDataAccess, CollisionRecordDataAccess>();
|
builder.Services.AddSingleton<ICollisionRecordDataAccess, CollisionRecordDataAccess>();
|
||||||
builder.Services.AddSingleton<ITaxRecordDataAccess, TaxRecordDataAccess>();
|
builder.Services.AddSingleton<ITaxRecordDataAccess, TaxRecordDataAccess>();
|
||||||
|
builder.Services.AddSingleton<IReminderRecordDataAccess, ReminderRecordDataAccess>();
|
||||||
builder.Services.AddSingleton<IFileHelper, FileHelper>();
|
builder.Services.AddSingleton<IFileHelper, FileHelper>();
|
||||||
|
|
||||||
|
if (!Directory.Exists("data"))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory("data");
|
||||||
|
}
|
||||||
|
|
||||||
//Additional JsonFile
|
//Additional JsonFile
|
||||||
builder.Configuration.AddJsonFile("userConfig.json", optional: true, reloadOnChange: true);
|
builder.Configuration.AddJsonFile(StaticHelper.UserConfigPath, optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
//Configure Auth
|
//Configure Auth
|
||||||
builder.Services.AddDataProtection();
|
builder.Services.AddDataProtection();
|
||||||
|
|||||||
47
README.md
47
README.md
@@ -11,4 +11,49 @@ Because nobody should have to deal with a homemade spreadsheet or a shoebox full
|
|||||||
- Bootstrap-DatePicker
|
- Bootstrap-DatePicker
|
||||||
- SweetAlert2
|
- SweetAlert2
|
||||||
- CsvHelper
|
- CsvHelper
|
||||||
- Chart.js
|
- Chart.js
|
||||||
|
|
||||||
|
## Docker Setup (Recommended)
|
||||||
|
1. Install Docker
|
||||||
|
2. Clone this repo
|
||||||
|
3. CHECK culture in Dockerfile, default is en_US
|
||||||
|
4. Run `docker build -t lubelogger -f Dockerfile .`
|
||||||
|
5. CHECK docker-compose.yml and make sure the mounting directories look correct.
|
||||||
|
6. If not using traefik, use docker-compose-notraefik.yml
|
||||||
|
7. Run `docker-compose up`
|
||||||
|
|
||||||
|
## Additional Docker Instructions
|
||||||
|
|
||||||
|
### manual
|
||||||
|
|
||||||
|
- build
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t hargata/lubelog:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
- run
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -d hargata/lubelog:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
add `-v` for persistent volumes as needed. Have a look at the docker-compose.yml for examples.
|
||||||
|
|
||||||
|
## docker-compose
|
||||||
|
|
||||||
|
- build image
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose build
|
||||||
|
```
|
||||||
|
|
||||||
|
- run
|
||||||
|
|
||||||
|
```
|
||||||
|
docker compose up
|
||||||
|
|
||||||
|
# or variant with traefik labels:
|
||||||
|
|
||||||
|
docker compose -f docker-compose.traefik.yml up
|
||||||
|
```
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
@if (enableAuth)
|
@if (enableAuth)
|
||||||
{
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button class="nav-link" onclick="performLogOut()">Logout</button>
|
<button class="nav-link" onclick="performLogOut()"><i class="bi bi-box-arrow-right me-2"></i>Logout</button>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</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.UseMPG">
|
||||||
<label class="form-check-label" for="useMPG">Use Imperial Units for Fuel Economy Calculations(Miles, Gallons)</label>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
|
|||||||
@@ -14,14 +14,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputUserPassword">Password</label>
|
<label for="inputUserPassword">Password</label>
|
||||||
<input type="password" id="inputUserPassword" 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">Remember Me</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()">Login</button>
|
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>Login</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,16 @@
|
|||||||
@{
|
@{
|
||||||
var useDarkMode = bool.Parse(Configuration["UseDarkMode"]);
|
var useDarkMode = bool.Parse(Configuration["UseDarkMode"]);
|
||||||
var enableCsvImports = bool.Parse(Configuration["EnableCsvImports"]);
|
var enableCsvImports = bool.Parse(Configuration["EnableCsvImports"]);
|
||||||
|
var shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
|
||||||
|
shortDatePattern = shortDatePattern.ToLower();
|
||||||
|
if (!shortDatePattern.Contains("dd"))
|
||||||
|
{
|
||||||
|
shortDatePattern = shortDatePattern.Replace("d", "dd");
|
||||||
|
}
|
||||||
|
if (!shortDatePattern.Contains("mm"))
|
||||||
|
{
|
||||||
|
shortDatePattern = shortDatePattern.Replace("m", "mm");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
<html lang="en" data-bs-theme="@(useDarkMode ? "dark" : "light")">
|
<html lang="en" data-bs-theme="@(useDarkMode ? "dark" : "light")">
|
||||||
<head>
|
<head>
|
||||||
@@ -28,6 +38,11 @@
|
|||||||
enableCsvImport : "@enableCsvImports" == "True"
|
enableCsvImport : "@enableCsvImports" == "True"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function getShortDatePattern() {
|
||||||
|
return {
|
||||||
|
pattern: "@shortDatePattern"
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
<script src="~/js/gasrecord.js" asp-append-version="true"></script>
|
<script src="~/js/gasrecord.js" asp-append-version="true"></script>
|
||||||
<script src="~/js/collisionrecord.js" asp-append-version="true"></script>
|
<script src="~/js/collisionrecord.js" asp-append-version="true"></script>
|
||||||
<script src="~/js/taxrecord.js" asp-append-version="true"></script>
|
<script src="~/js/taxrecord.js" asp-append-version="true"></script>
|
||||||
|
<script src="~/js/reminderrecord.js" asp-append-version="true"></script>
|
||||||
<script src="~/lib/chart-js/chart.umd.js"></script>
|
<script src="~/lib/chart-js/chart.umd.js"></script>
|
||||||
}
|
}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -35,6 +36,9 @@
|
|||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" 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" 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>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><div id="reminderBellDiv" style="display:inline-flex;"><i id="reminderBell" class="bi bi-bell me-2"></i></div>Reminders</button>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link" 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>Reports</button>
|
<button class="nav-link" 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>Reports</button>
|
||||||
</li>
|
</li>
|
||||||
@@ -65,6 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane fade" id="accident-tab-pane" role="tabpanel" tabindex="0"></div>
|
<div class="tab-pane fade" id="accident-tab-pane" role="tabpanel" tabindex="0"></div>
|
||||||
|
<div class="tab-pane fade" id="reminder-tab-pane" role="tabpanel" tabindex="0"></div>
|
||||||
<div class="tab-pane fade" id="report-tab-pane" role="tabpanel" tabindex="0"></div>
|
<div class="tab-pane fade" id="report-tab-pane" role="tabpanel" tabindex="0"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,6 +85,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal fade" id="reminderRecordModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content" id="reminderRecordModalContent">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function GetVehicleId() {
|
function GetVehicleId() {
|
||||||
return { vehicleId: @Model.Id};
|
return { vehicleId: @Model.Id};
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
@model CollisionRecordInput
|
@model CollisionRecordInput
|
||||||
|
@{
|
||||||
|
var isNew = Model.Id == 0;
|
||||||
|
}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">@(Model.Id == 0 ? "Add New Repair Record" : "Edit Repair Record")</h5>
|
<h5 class="modal-title">@(isNew ? "Add New Repair Record" : "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">
|
||||||
@@ -11,15 +14,15 @@
|
|||||||
<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">Date</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="collisionRecordDate" class="form-control" value="@Model.Date">
|
<input type="text" id="collisionRecordDate" class="form-control" placeholder="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">Odometer</label>
|
||||||
<input type="number" id="collisionRecordMileage" class="form-control" value="@Model.Mileage">
|
<input type="number" id="collisionRecordMileage" class="form-control" placeholder="Odometer reading when repaired" value="@(isNew ? "" : Model.Mileage)">
|
||||||
<label for="collisionRecordDescription">Description</label>
|
<label for="collisionRecordDescription">Description</label>
|
||||||
<input type="text" id="collisionRecordDescription" class="form-control" value="@Model.Description">
|
<input type="text" id="collisionRecordDescription" class="form-control" placeholder="Description of item(s) repaired(i.e. Alternator)" value="@Model.Description">
|
||||||
<label for="collisionRecordCost">Cost</label>
|
<label for="collisionRecordCost">Cost</label>
|
||||||
<input type="number" id="collisionRecordCost" class="form-control" value="@Model.Cost">
|
<input type="number" id="collisionRecordCost" class="form-control" placeholder="Cost of the repair" value="@(isNew ? "" : Model.Cost)">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="collisionRecordNotes">Notes(optional)</label>
|
<label for="collisionRecordNotes">Notes(optional)</label>
|
||||||
@@ -41,6 +44,15 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@if (isNew)
|
||||||
|
{
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
|
||||||
|
<label class="form-check-label" for="addReminderCheck">
|
||||||
|
Add Reminder
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<label for="collisionRecordFiles">Upload documents(optional)</label>
|
<label for="collisionRecordFiles">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">
|
||||||
}
|
}
|
||||||
@@ -50,16 +62,16 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@if (Model.Id > 0)
|
@if (!isNew)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger" onclick="deleteCollisionRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
|
<button type="button" class="btn btn-danger" onclick="deleteCollisionRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-secondary" onclick="hideAddCollisionRecordModal()">Cancel</button>
|
<button type="button" class="btn btn-secondary" onclick="hideAddCollisionRecordModal()">Cancel</button>
|
||||||
@if (Model.Id == 0)
|
@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()">Add New Repair Record</button>
|
||||||
}
|
}
|
||||||
else if (Model.Id > 0)
|
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)">Edit Repair Record</button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,33 @@
|
|||||||
@inject IConfiguration Configuration
|
@inject IConfiguration Configuration
|
||||||
|
@model GasRecordViewModelContainer
|
||||||
@{
|
@{
|
||||||
var enableCsvImports = bool.Parse(Configuration[nameof(UserConfig.EnableCsvImports)]);
|
var enableCsvImports = bool.Parse(Configuration[nameof(UserConfig.EnableCsvImports)]);
|
||||||
var useMPG = bool.Parse(Configuration[nameof(UserConfig.UseMPG)]);
|
var useMPG = bool.Parse(Configuration[nameof(UserConfig.UseMPG)]);
|
||||||
|
var useKwh = Model.UseKwh;
|
||||||
|
string consumptionUnit;
|
||||||
|
string fuelEconomyUnit;
|
||||||
|
if (useKwh)
|
||||||
|
{
|
||||||
|
consumptionUnit = "kWh";
|
||||||
|
fuelEconomyUnit = useMPG ? "mi/kWh" : "kWh/100km";
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
consumptionUnit = useMPG ? "gal" : "l";
|
||||||
|
fuelEconomyUnit = useMPG ? "mpg" : "l/100km";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@model List<GasRecordViewModel>
|
|
||||||
<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.Count()}")</span>
|
<span class="ms-2 badge bg-success">@($"# of Gas Records: {Model.GasRecords.Count()}")</span>
|
||||||
@if (Model.Count() > 1)
|
@if (Model.GasRecords.Where(x => x.MilesPerGallon > 0).Any())
|
||||||
{
|
{
|
||||||
<span class="ms-2 badge bg-primary">@($"Average Fuel Economy: {Model.Where(y => y.MilesPerGallon > 0)?.Average(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
<span class="ms-2 badge bg-primary">@($"Average Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Average(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||||
<span class="ms-2 badge bg-primary">@($"Min Fuel Economy: {Model.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
<span class="ms-2 badge bg-primary">@($"Min Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||||
<span class="ms-2 badge bg-primary">@($"Max Fuel Economy: {Model.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
<span class="ms-2 badge bg-primary">@($"Max Fuel Economy: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||||
}
|
}
|
||||||
<span class="ms-2 badge bg-success">@($"Total Fuel Consumed: {Model.Sum(x=>x.Gallons).ToString("F")}")</span>
|
<span class="ms-2 badge bg-success">@($"Total Fuel Consumed: {Model.GasRecords.Sum(x => x.Gallons).ToString("F")}")</span>
|
||||||
<span class="ms-2 badge bg-success">@($"Total Cost: {Model.Sum(x => x.Cost).ToString("C")}")</span>
|
<span class="ms-2 badge bg-success">@($"Total Cost: {Model.GasRecords.Sum(x => x.Cost).ToString("C3")}")</span>
|
||||||
</div>
|
</div>
|
||||||
@if (enableCsvImports)
|
@if (enableCsvImports)
|
||||||
{
|
{
|
||||||
@@ -40,22 +52,22 @@
|
|||||||
<tr class="d-flex">
|
<tr class="d-flex">
|
||||||
<th scope="col" class="col-2">Date Refueled</th>
|
<th scope="col" class="col-2">Date Refueled</th>
|
||||||
<th scope="col" class="col-2">Odometer(@(useMPG ? "mi." : "km"))</th>
|
<th scope="col" class="col-2">Odometer(@(useMPG ? "mi." : "km"))</th>
|
||||||
<th scope="col" class="col-2">Consumption(@(useMPG ? "gal" : "l"))</th>
|
<th scope="col" class="col-2">Consumption(@(consumptionUnit))</th>
|
||||||
<th scope="col" class="col-2">Fuel Economy(@(useMPG ? "mpg" : "l/100km"))</th>
|
<th scope="col" class="col-4">Fuel Economy(@(fuelEconomyUnit))</th>
|
||||||
<th scope="col" class="col-2">Cost</th>
|
<th scope="col" class="col-1">Cost</th>
|
||||||
<th scope="col" class="col-2">Unit Cost</th>
|
<th scope="col" class="col-1">Unit Cost</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (GasRecordViewModel gasRecord in Model)
|
@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)">
|
||||||
<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">@gasRecord.Mileage</td>
|
||||||
<td class="col-2">@gasRecord.Gallons.ToString("F")</td>
|
<td class="col-2">@gasRecord.Gallons.ToString("F")</td>
|
||||||
<td class="col-2">@gasRecord.MilesPerGallon.ToString("F")</td>
|
<td class="col-4">@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
|
||||||
<td class="col-2">@gasRecord.Cost.ToString("C")</td>
|
<td class="col-1">@gasRecord.Cost.ToString("C3")</td>
|
||||||
<td class="col-2">@gasRecord.CostPerGallon.ToString("C")</td>
|
<td class="col-1">@gasRecord.CostPerGallon.ToString("C3")</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
@inject IConfiguration Configuration
|
@inject IConfiguration Configuration
|
||||||
|
@model GasRecordInputContainer
|
||||||
@{
|
@{
|
||||||
var useMPG = bool.Parse(Configuration[nameof(UserConfig.UseMPG)]);
|
var useMPG = bool.Parse(Configuration[nameof(UserConfig.UseMPG)]);
|
||||||
|
var useKwh = Model.UseKwh;
|
||||||
|
var isNew = Model.GasRecord.Id == 0;
|
||||||
|
string consumptionUnit;
|
||||||
|
if (useKwh)
|
||||||
|
{
|
||||||
|
consumptionUnit = "kWh";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
consumptionUnit = useMPG ? "gallons" : "liters";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@model GasRecordInput
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">@(Model.Id == 0 ? "Add New Gas Record" : "Edit Gas Record")</h5>
|
<h5 class="modal-title">@(isNew ? "Add New Gas Record" : "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">
|
||||||
@@ -15,22 +26,26 @@
|
|||||||
<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">Date</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="gasRecordDate" class="form-control" value="@Model.Date">
|
<input type="text" id="gasRecordDate" placeholder="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(@(useMPG ? "miles" : "kilometers"))</label>
|
<label for="gasRecordMileage">Odometer Reading(@(useMPG ? "miles" : "kilometers"))</label>
|
||||||
<input type="number" id="gasRecordMileage" class="form-control" value="@Model.Mileage">
|
<input type="number" id="gasRecordMileage" class="form-control" placeholder="Odometer reading when refueled" value="@(isNew ? "" : Model.GasRecord.Mileage)">
|
||||||
<label for="gasRecordGallons">Fuel Consumption(@(useMPG ? "gallons" : "liters"))</label>
|
<label for="gasRecordGallons">Fuel Consumption(@(consumptionUnit))</label>
|
||||||
<input type="text" id="gasRecordGallons" class="form-control" value="@Model.Gallons">
|
<input type="text" id="gasRecordGallons" class="form-control" placeholder="Amount of gas refueled" value="@(isNew ? "" : Model.GasRecord.Gallons)">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="gasIsFillToFull" checked="@Model.GasRecord.IsFillToFull">
|
||||||
|
<label class="form-check-label" for="gasIsFillToFull">Is Filled To Full</label>
|
||||||
|
</div>
|
||||||
<label for="GasRecordCost">Cost</label>
|
<label for="GasRecordCost">Cost</label>
|
||||||
<input type="number" id="gasRecordCost" class="form-control" value="@Model.Cost">
|
<input type="number" id="gasRecordCost" class="form-control" placeholder="Cost of gas refueled" value="@(isNew ? "" : Model.GasRecord.Cost)">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
@if (Model.Files.Any())
|
@if (Model.GasRecord.Files.Any())
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
<label>Uploaded Documents</label>
|
<label>Uploaded Documents</label>
|
||||||
@foreach (UploadedFiles filesUploaded in Model.Files)
|
@foreach (UploadedFiles filesUploaded in Model.GasRecord.Files)
|
||||||
{
|
{
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<a type="button" class="btn btn-link" href="@filesUploaded.Location" target="_blank">@filesUploaded.Name</a>
|
<a type="button" class="btn btn-link" href="@filesUploaded.Location" target="_blank">@filesUploaded.Name</a>
|
||||||
@@ -52,16 +67,16 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@if (Model.Id > 0)
|
@if (!isNew)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger" onclick="deleteGasRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
|
<button type="button" class="btn btn-danger" onclick="deleteGasRecord(@Model.GasRecord.Id)" style="margin-right:auto;">Delete</button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-secondary" onclick="hideAddGasRecordModal()">Cancel</button>
|
<button type="button" class="btn btn-secondary" onclick="hideAddGasRecordModal()">Cancel</button>
|
||||||
@if (Model.Id == 0)
|
@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()">Add New Gas Record</button>
|
||||||
}
|
}
|
||||||
else if (Model.Id > 0)
|
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)">Edit Gas Record</button>
|
||||||
}
|
}
|
||||||
@@ -70,12 +85,12 @@
|
|||||||
var uploadedFiles = [];
|
var uploadedFiles = [];
|
||||||
getUploadedFilesFromModel();
|
getUploadedFilesFromModel();
|
||||||
function getUploadedFilesFromModel() {
|
function getUploadedFilesFromModel() {
|
||||||
@foreach (UploadedFiles filesUploaded in Model.Files)
|
@foreach (UploadedFiles filesUploaded in Model.GasRecord.Files)
|
||||||
{
|
{
|
||||||
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
|
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getGasRecordModelData(){
|
function getGasRecordModelData(){
|
||||||
return {id: @Model.Id}
|
return { id: @Model.GasRecord.Id}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
68
Views/Vehicle/_ReminderRecordModal.cshtml
Normal file
68
Views/Vehicle/_ReminderRecordModal.cshtml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
@model ReminderRecordInput
|
||||||
|
@{
|
||||||
|
var isNew = Model.Id == 0;
|
||||||
|
}
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">@(isNew ? "Add New Reminder" : "Edit Reminder")</h5>
|
||||||
|
<button type="button" class="btn-close" onclick="hideAddReminderRecordModal()" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-12" id="reminderOptions">
|
||||||
|
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||||
|
<label for="reminderDescription">Description</label>
|
||||||
|
<input type="text" id="reminderDescription" class="form-control" placeholder="Reminder Description" value="@Model.Description">
|
||||||
|
<label>Remind me on:</label>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricDate" value="@(ReminderMetric.Date)" checked="@(Model.Metric == ReminderMetric.Date)">
|
||||||
|
<label class="form-check-label" for="reminderMetricDate">Date</label>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="reminderDate" class="form-control" placeholder="Future Date" value="@Model.Date">
|
||||||
|
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricOdometer" value="@(ReminderMetric.Odometer)" checked="@(Model.Metric == ReminderMetric.Odometer)">
|
||||||
|
<label class="form-check-label" for="reminderMetricOdometer">Odometer</label>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="number" id="reminderMileage" class="form-control" placeholder="Future Odometer Reading" value="@(isNew ? "" : Model.Mileage)">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<button type="button" class="btn btn-sm btn-primary" onclick="appendMileageToOdometer(500)">+500</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio" name="reminderMetricOptions" id="reminderMetricBoth" value="@(ReminderMetric.Both)" checked="@(Model.Metric == ReminderMetric.Both)">
|
||||||
|
<label class="form-check-label" for="reminderMetricBoth">Whichever comes first</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-12">
|
||||||
|
<label for="reminderNotes">Notes(optional)</label>
|
||||||
|
<textarea id="reminderNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
@if (!isNew)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-danger" onclick="deleteReminderRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
|
||||||
|
}
|
||||||
|
<button type="button" class="btn btn-secondary" onclick="hideAddReminderRecordModal()">Cancel</button>
|
||||||
|
@if (isNew)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" onclick="saveReminderRecordToVehicle()">Add New Reminder</button>
|
||||||
|
}
|
||||||
|
else if (!isNew)
|
||||||
|
{
|
||||||
|
<button type="button" class="btn btn-primary" onclick="saveReminderRecordToVehicle(true)">Edit Reminder</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
function getReminderRecordModelData() {
|
||||||
|
return { id: @Model.Id}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
70
Views/Vehicle/_ReminderRecords.cshtml
Normal file
70
Views/Vehicle/_ReminderRecords.cshtml
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
@model List<ReminderRecordViewModel>
|
||||||
|
<div class="row">
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<div class="d-flex align-items-center flex-wrap">
|
||||||
|
<span class="ms-2 badge bg-success">@($"# of Reminders: {Model.Count()}")</span>
|
||||||
|
<span class="ms-2 badge bg-secondary">@($"Past Due: {Model.Where(x => x.Urgency == ReminderUrgency.PastDue).Count()}")</span>
|
||||||
|
<span class="ms-2 badge bg-danger">@($"Very Urgent: {Model.Where(x=>x.Urgency == ReminderUrgency.VeryUrgent).Count()}")</span>
|
||||||
|
<span class="ms-2 badge bg-warning">@($"Urgent: {Model.Where(x => x.Urgency == ReminderUrgency.Urgent).Count()}")</span>
|
||||||
|
<span class="ms-2 badge bg-success">@($"Not Urgent: {Model.Where(x => x.Urgency == ReminderUrgency.NotUrgent).Count()}")</span>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row vehicleDetailTabContainer">
|
||||||
|
<div class="col-12">
|
||||||
|
<table class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr class="d-flex">
|
||||||
|
<th scope="col" class="col-1">Urgency</th>
|
||||||
|
<th scope="col" class="col-2">Metric</th>
|
||||||
|
<th scope="col" class="col-5">Description</th>
|
||||||
|
<th scope="col" class="col-3">Notes</th>
|
||||||
|
<th scope="col" class="col-1">Delete</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (ReminderRecordViewModel reminderRecord in Model)
|
||||||
|
{
|
||||||
|
<tr class="d-flex" style="cursor:pointer;" onclick="showEditReminderRecordModal(@reminderRecord.Id)">
|
||||||
|
@if (reminderRecord.Urgency == ReminderUrgency.VeryUrgent)
|
||||||
|
{
|
||||||
|
<td class="col-1"><span class="badge text-bg-danger">Very Urgent</span></td>
|
||||||
|
}
|
||||||
|
else if (reminderRecord.Urgency == ReminderUrgency.Urgent)
|
||||||
|
{
|
||||||
|
<td class="col-1"><span class="badge text-bg-warning">Urgent</span></td>
|
||||||
|
}
|
||||||
|
else if (reminderRecord.Urgency == ReminderUrgency.PastDue)
|
||||||
|
{
|
||||||
|
<td class="col-1"><span class="badge text-bg-secondary">Past Due</span></td>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<td class="col-1"><span class="badge text-bg-success">Not Urgent</span></td>
|
||||||
|
}
|
||||||
|
@if (reminderRecord.Metric == ReminderMetric.Date)
|
||||||
|
{
|
||||||
|
<td class="col-2">@reminderRecord.Date.ToShortDateString()</td>
|
||||||
|
}
|
||||||
|
else if (reminderRecord.Metric == ReminderMetric.Odometer)
|
||||||
|
{
|
||||||
|
<td class="col-2">@reminderRecord.Mileage</td>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<td class="col-2">@reminderRecord.Metric</td>
|
||||||
|
}
|
||||||
|
<td class="col-5">@reminderRecord.Description</td>
|
||||||
|
<td class="col-3 text-truncate">@reminderRecord.Notes</td>
|
||||||
|
<td class="col-1 text-truncate">
|
||||||
|
<button type="button" class="btn btn-danger" onclick="deleteReminderRecord(@reminderRecord.Id, this)"><i class="bi bi-trash"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
@model ServiceRecordInput
|
@model ServiceRecordInput
|
||||||
|
@{
|
||||||
|
var isNew = Model.Id == 0;
|
||||||
|
}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">@(Model.Id == 0 ? "Add New Service Record" : "Edit Service Record")</h5>
|
<h5 class="modal-title">@(isNew ? "Add New Service Record" : "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">
|
||||||
@@ -11,15 +14,15 @@
|
|||||||
<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">Date</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="serviceRecordDate" class="form-control" value="@Model.Date">
|
<input type="text" id="serviceRecordDate" class="form-control" placeholder="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">Odometer</label>
|
||||||
<input type="number" id="serviceRecordMileage" class="form-control" value="@Model.Mileage">
|
<input type="number" id="serviceRecordMileage" class="form-control" placeholder="Odometer reading when serviced" value="@(isNew ? "" : Model.Mileage)">
|
||||||
<label for="serviceRecordDescription">Description</label>
|
<label for="serviceRecordDescription">Description</label>
|
||||||
<input type="text" id="serviceRecordDescription" class="form-control" value="@Model.Description">
|
<input type="text" id="serviceRecordDescription" class="form-control" placeholder="Description of item(s) serviced(i.e. Oil Change)" value="@Model.Description">
|
||||||
<label for="serviceRecordCost">Cost</label>
|
<label for="serviceRecordCost">Cost</label>
|
||||||
<input type="number" id="serviceRecordCost" class="form-control" value="@Model.Cost">
|
<input type="number" id="serviceRecordCost" class="form-control" placeholder="Cost of the service" value="@(isNew ? "" : Model.Cost)">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="serviceRecordNotes">Notes(optional)</label>
|
<label for="serviceRecordNotes">Notes(optional)</label>
|
||||||
@@ -41,6 +44,15 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@if (isNew)
|
||||||
|
{
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
|
||||||
|
<label class="form-check-label" for="addReminderCheck">
|
||||||
|
Add Reminder
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<label for="serviceRecordFiles">Upload documents(optional)</label>
|
<label for="serviceRecordFiles">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">
|
||||||
}
|
}
|
||||||
@@ -50,16 +62,16 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@if (Model.Id > 0)
|
@if (!isNew)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger" onclick="deleteServiceRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
|
<button type="button" class="btn btn-danger" onclick="deleteServiceRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-secondary" onclick="hideAddServiceRecordModal()">Cancel</button>
|
<button type="button" class="btn btn-secondary" onclick="hideAddServiceRecordModal()">Cancel</button>
|
||||||
@if (Model.Id == 0)
|
@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()">Add New Service Record</button>
|
||||||
}
|
}
|
||||||
else if (Model.Id > 0)
|
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)">Edit Service Record</button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
@model TaxRecordInput
|
@model TaxRecordInput
|
||||||
|
@{
|
||||||
|
var isNew = Model.Id == 0;
|
||||||
|
}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title">@(Model.Id == 0 ? "Add New Tax Record" : "Edit Tax Record")</h5>
|
<h5 class="modal-title">@(isNew ? "Add New Tax Record" : "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">
|
||||||
@@ -11,13 +14,13 @@
|
|||||||
<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">Date</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="taxRecordDate" class="form-control" value="@Model.Date">
|
<input type="text" id="taxRecordDate" class="form-control" placeholder="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">Description</label>
|
||||||
<input type="text" id="taxRecordDescription" class="form-control" value="@Model.Description">
|
<input type="text" id="taxRecordDescription" class="form-control" placeholder="Description of tax paid(i.e. Registration)" value="@Model.Description">
|
||||||
<label for="taxRecordCost">Cost</label>
|
<label for="taxRecordCost">Cost</label>
|
||||||
<input type="number" id="taxRecordCost" class="form-control" value="@Model.Cost">
|
<input type="number" id="taxRecordCost" class="form-control" placeholder="Cost of tax paid" value="@(isNew? "" : Model.Cost)">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="taxRecordNotes">Notes(optional)</label>
|
<label for="taxRecordNotes">Notes(optional)</label>
|
||||||
@@ -39,6 +42,15 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@if (isNew)
|
||||||
|
{
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" value="" id="addReminderCheck">
|
||||||
|
<label class="form-check-label" for="addReminderCheck">
|
||||||
|
Add Reminder
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<label for="taxRecordFiles">Upload documents(optional)</label>
|
<label for="taxRecordFiles">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">
|
||||||
}
|
}
|
||||||
@@ -48,16 +60,16 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@if (Model.Id > 0)
|
@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;">Delete</button>
|
||||||
}
|
}
|
||||||
<button type="button" class="btn btn-secondary" onclick="hideAddTaxRecordModal()">Cancel</button>
|
<button type="button" class="btn btn-secondary" onclick="hideAddTaxRecordModal()">Cancel</button>
|
||||||
@if (Model.Id == 0)
|
@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()">Add New Tax Record</button>
|
||||||
}
|
}
|
||||||
else if (Model.Id > 0)
|
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)">Edit Tax Record</button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
@model Vehicle
|
@model Vehicle
|
||||||
@{
|
@{
|
||||||
|
var isNew = Model.Id == 0;
|
||||||
if (Model.ImageLocation == "/defaults/noimage.png")
|
if (Model.ImageLocation == "/defaults/noimage.png")
|
||||||
{
|
{
|
||||||
Model.ImageLocation = "";
|
Model.ImageLocation = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="addVehicleModalLabel">@(Model.Id == 0 ? "Add New Vehicle" : "Edit Vehicle")</h5>
|
<h5 class="modal-title" id="addVehicleModalLabel">@(isNew ? "Add New Vehicle" : "Edit Vehicle")</h5>
|
||||||
</div>
|
</div>
|
||||||
<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">Year</label>
|
||||||
<input type="number" id="inputYear" class="form-control" value="@(Model.Year > 0 ? Model.Year : "")">
|
<input type="number" id="inputYear" class="form-control" placeholder="Year(must be after 1900)" value="@(isNew ? "" : Model.Year)">
|
||||||
<label for="inputMake">Make</label>
|
<label for="inputMake">Make</label>
|
||||||
<input type="text" id="inputMake" class="form-control" value="@Model.Make">
|
<input type="text" id="inputMake" class="form-control" placeholder="Make" value="@Model.Make">
|
||||||
<label for="inputModel">Model</label>
|
<label for="inputModel">Model</label>
|
||||||
<input type="text" id="inputModel" class="form-control" value="@Model.Model">
|
<input type="text" id="inputModel" class="form-control" placeholder="Model" value="@Model.Model">
|
||||||
<label for="inputLicensePlate">License Plate</label>
|
<label for="inputLicensePlate">License Plate</label>
|
||||||
<input type="text" id="inputLicensePlate" class="form-control" value="@Model.LicensePlate">
|
<input type="text" id="inputLicensePlate" class="form-control" placeholder="License Plate" value="@Model.LicensePlate">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="inputIsElectric" checked="@Model.IsElectric">
|
||||||
|
<label class="form-check-label" for="inputIsElectric">Electric Vehicle</label>
|
||||||
|
</div>
|
||||||
@if (!string.IsNullOrWhiteSpace(Model.ImageLocation))
|
@if (!string.IsNullOrWhiteSpace(Model.ImageLocation))
|
||||||
{
|
{
|
||||||
<label for="inputImage">Replace picture(optional)</label>
|
<label for="inputImage">Replace picture(optional)</label>
|
||||||
@@ -32,11 +37,11 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@if (Model.Id == 0)
|
@if (isNew)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-secondary" onclick="hideAddVehicleModal()">Cancel</button>
|
<button type="button" class="btn btn-secondary" onclick="hideAddVehicleModal()">Cancel</button>
|
||||||
<button type="button" onclick="saveVehicle(false)" class="btn btn-primary">Add New Vehicle</button>
|
<button type="button" onclick="saveVehicle(false)" class="btn btn-primary">Add New Vehicle</button>
|
||||||
} else if (Model.Id > 0)
|
} else if (!isNew)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-secondary" onclick="hideEditVehicleModal()">Cancel</button>
|
<button type="button" class="btn btn-secondary" onclick="hideEditVehicleModal()">Cancel</button>
|
||||||
<button type="button" onclick="saveVehicle(true)" class="btn btn-primary">Save Vehicle</button>
|
<button type="button" onclick="saveVehicle(true)" class="btn btn-primary">Save Vehicle</button>
|
||||||
|
|||||||
@@ -5,13 +5,6 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Kestrel": {
|
|
||||||
"Endpoints": {
|
|
||||||
"http": {
|
|
||||||
"Url": "http://localhost:5000"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"UseDarkMode": false,
|
"UseDarkMode": false,
|
||||||
"EnableCsvImports": true,
|
"EnableCsvImports": true,
|
||||||
|
|||||||
1
config/userConfig.json
Normal file
1
config/userConfig.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"UseDarkMode":true,"UsekWh":false,"EnableCsvImports":false,"UseMPG":true,"UseDescending":false,"EnableAuth":false,"UserNameHash":"","UserPasswordHash":""}
|
||||||
50
docker-compose.traefik.yml
Normal file
50
docker-compose.traefik.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
version: "3.4"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: ghcr.io/hargata/lubelogger:latest
|
||||||
|
build: .
|
||||||
|
restart: unless-stopped
|
||||||
|
# volumes used to keep data persistent
|
||||||
|
volumes:
|
||||||
|
- config:/App/config
|
||||||
|
- data:/App/data
|
||||||
|
- documents:/App/wwwroot/documents
|
||||||
|
- images:/App/wwwroot/images
|
||||||
|
- log:/App/log
|
||||||
|
- keys:/root/.aspnet/DataProtection-Keys
|
||||||
|
# expose port and/or use serving via traefik
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
# traefik configurations, including networks can be commented out if not needed
|
||||||
|
networks:
|
||||||
|
- traefik-ingress
|
||||||
|
labels:
|
||||||
|
## Traefik General
|
||||||
|
# We set 'enable by default' to false, so this tells Traefik we want it to connect here
|
||||||
|
traefik.enable: true
|
||||||
|
# define network for traefik<>app communication
|
||||||
|
traefik.docker.network: traefik-ingress
|
||||||
|
## HTTP Routers
|
||||||
|
traefik.http.routers.whoami.entrypoints: https
|
||||||
|
traefik.http.routers.whoami.rule: Host(`lubelog.mydomain.tld`)
|
||||||
|
## Middlewares
|
||||||
|
#traefik.http.routers.whoami.middlewares: authentik@docker
|
||||||
|
# none
|
||||||
|
## HTTP Services
|
||||||
|
traefik.http.services.whoami.loadbalancer.server.port: 5000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
config:
|
||||||
|
data:
|
||||||
|
documents:
|
||||||
|
images:
|
||||||
|
log:
|
||||||
|
keys:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik-ingress:
|
||||||
|
external: true
|
||||||
29
docker-compose.yml
Normal file
29
docker-compose.yml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
version: "3.4"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: ghcr.io/hargata/lubelogger:latest
|
||||||
|
build: .
|
||||||
|
restart: unless-stopped
|
||||||
|
# volumes used to keep data persistent
|
||||||
|
volumes:
|
||||||
|
- config:/App/config
|
||||||
|
- data:/App/data
|
||||||
|
- documents:/App/wwwroot/documents
|
||||||
|
- images:/App/wwwroot/images
|
||||||
|
- log:/App/log
|
||||||
|
- keys:/root/.aspnet/DataProtection-Keys
|
||||||
|
# expose port and/or use serving via traefik
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
config:
|
||||||
|
data:
|
||||||
|
documents:
|
||||||
|
images:
|
||||||
|
log:
|
||||||
|
keys:
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"UseDarkMode":true,"EnableCsvImports":true,"UseMPG":true,"UseDescending":false,"EnableAuth":false,"UserNameHash":"","UserPasswordHash":""}
|
|
||||||
@@ -55,4 +55,48 @@ html {
|
|||||||
.display-7 {
|
.display-7 {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bell-shake {
|
||||||
|
animation: bellshake .5s;
|
||||||
|
backface-visibility: hidden;
|
||||||
|
transform-origin: top center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bellshake {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
15% {
|
||||||
|
transform: rotate(5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
30% {
|
||||||
|
transform: rotate(-5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
45% {
|
||||||
|
transform: rotate(4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
60% {
|
||||||
|
transform: rotate(-4deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
85% {
|
||||||
|
transform: rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
92% {
|
||||||
|
transform: rotate(1deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
$("#collisionRecordModalContent").html(data);
|
$("#collisionRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#collisionRecordDate').datepicker({
|
$('#collisionRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#collisionRecordModal').modal('show');
|
$('#collisionRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -16,7 +17,8 @@ function showEditCollisionRecordModal(collisionRecordId) {
|
|||||||
$("#collisionRecordModalContent").html(data);
|
$("#collisionRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#collisionRecordDate').datepicker({
|
$('#collisionRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#collisionRecordModal').modal('show');
|
$('#collisionRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -64,6 +66,9 @@ function saveCollisionRecordToVehicle(isEdit) {
|
|||||||
successToast(isEdit ? "Repair Record Updated" : "Repair Record Added.");
|
successToast(isEdit ? "Repair Record Updated" : "Repair Record Added.");
|
||||||
hideAddCollisionRecordModal();
|
hideAddCollisionRecordModal();
|
||||||
getVehicleCollisionRecords(formValues.vehicleId);
|
getVehicleCollisionRecords(formValues.vehicleId);
|
||||||
|
if (formValues.addReminderRecord) {
|
||||||
|
setTimeout(function () { showAddReminderModal(formValues); }, 500);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorToast("An error has occurred, please try again later.");
|
errorToast("An error has occurred, please try again later.");
|
||||||
}
|
}
|
||||||
@@ -77,6 +82,7 @@ function getAndValidateCollisionRecordValues() {
|
|||||||
var collisionNotes = $("#collisionRecordNotes").val();
|
var collisionNotes = $("#collisionRecordNotes").val();
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var collisionRecordId = getCollisionRecordModelData().id;
|
var collisionRecordId = getCollisionRecordModelData().id;
|
||||||
|
var addReminderRecord = $("#addReminderCheck").is(":checked");
|
||||||
//validation
|
//validation
|
||||||
var hasError = false;
|
var hasError = false;
|
||||||
if (collisionDate.trim() == '') { //eliminates whitespace.
|
if (collisionDate.trim() == '') { //eliminates whitespace.
|
||||||
@@ -112,7 +118,8 @@ function getAndValidateCollisionRecordValues() {
|
|||||||
description: collisionDescription,
|
description: collisionDescription,
|
||||||
cost: collisionCost,
|
cost: collisionCost,
|
||||||
notes: collisionNotes,
|
notes: collisionNotes,
|
||||||
files: uploadedFiles
|
files: uploadedFiles,
|
||||||
|
addReminderRecord: addReminderRecord
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function deleteCollisionRecordFile(fileLocation, event) {
|
function deleteCollisionRecordFile(fileLocation, event) {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
$("#gasRecordModalContent").html(data);
|
$("#gasRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#gasRecordDate').datepicker({
|
$('#gasRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#gasRecordModal').modal('show');
|
$('#gasRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -16,7 +17,8 @@ function showEditGasRecordModal(gasRecordId) {
|
|||||||
$("#gasRecordModalContent").html(data);
|
$("#gasRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#gasRecordDate').datepicker({
|
$('#gasRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#gasRecordModal').modal('show');
|
$('#gasRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -74,6 +76,7 @@ function getAndValidateGasRecordValues() {
|
|||||||
var gasMileage = $("#gasRecordMileage").val();
|
var gasMileage = $("#gasRecordMileage").val();
|
||||||
var gasGallons = $("#gasRecordGallons").val();
|
var gasGallons = $("#gasRecordGallons").val();
|
||||||
var gasCost = $("#gasRecordCost").val();
|
var gasCost = $("#gasRecordCost").val();
|
||||||
|
var gasIsFillToFull = $("#gasIsFillToFull").is(":checked");
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var gasRecordId = getGasRecordModelData().id;
|
var gasRecordId = getGasRecordModelData().id;
|
||||||
//validation
|
//validation
|
||||||
@@ -110,7 +113,8 @@ function getAndValidateGasRecordValues() {
|
|||||||
mileage: gasMileage,
|
mileage: gasMileage,
|
||||||
gallons: gasGallons,
|
gallons: gasGallons,
|
||||||
cost: gasCost,
|
cost: gasCost,
|
||||||
files: uploadedFiles
|
files: uploadedFiles,
|
||||||
|
isFillToFull: gasIsFillToFull
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function deleteGasRecordFile(fileLocation, event) {
|
function deleteGasRecordFile(fileLocation, event) {
|
||||||
|
|||||||
@@ -9,4 +9,9 @@
|
|||||||
errorToast("Invalid Login Credentials, please try again.");
|
errorToast("Invalid Login Credentials, please try again.");
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
function handlePasswordKeyPress(event) {
|
||||||
|
if (event.keyCode == 13) {
|
||||||
|
performLogin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
124
wwwroot/js/reminderrecord.js
Normal file
124
wwwroot/js/reminderrecord.js
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
function showEditReminderRecordModal(reminderId) {
|
||||||
|
$.get(`/Vehicle/GetReminderRecordForEditById?reminderRecordId=${reminderId}`, function (data) {
|
||||||
|
if (data) {
|
||||||
|
$("#reminderRecordModalContent").html(data);
|
||||||
|
$('#reminderDate').datepicker({
|
||||||
|
startDate: "+0d"
|
||||||
|
});
|
||||||
|
$("#reminderRecordModal").modal("show");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function hideAddReminderRecordModal() {
|
||||||
|
$('#reminderRecordModal').modal('hide');
|
||||||
|
}
|
||||||
|
function deleteReminderRecord(reminderRecordId, e) {
|
||||||
|
if (e != undefined) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
$("#workAroundInput").show();
|
||||||
|
Swal.fire({
|
||||||
|
title: "Confirm Deletion?",
|
||||||
|
text: "Deleted Reminders cannot be restored.",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: "Delete",
|
||||||
|
confirmButtonColor: "#dc3545"
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.isConfirmed) {
|
||||||
|
$.post(`/Vehicle/DeleteReminderRecordById?reminderRecordId=${reminderRecordId}`, function (data) {
|
||||||
|
if (data) {
|
||||||
|
hideAddReminderRecordModal();
|
||||||
|
successToast("Reminder Deleted");
|
||||||
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
|
getVehicleReminders(vehicleId);
|
||||||
|
} else {
|
||||||
|
errorToast("An error has occurred, please try again later.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$("#workAroundInput").hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function saveReminderRecordToVehicle(isEdit) {
|
||||||
|
//get values
|
||||||
|
var formValues = getAndValidateReminderRecordValues();
|
||||||
|
//validate
|
||||||
|
if (formValues.hasError) {
|
||||||
|
errorToast("Please check the form data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//save to db.
|
||||||
|
$.post('/Vehicle/SaveReminderRecordToVehicleId', { reminderRecord: formValues }, function (data) {
|
||||||
|
if (data) {
|
||||||
|
successToast(isEdit ? "Reminder Updated" : "Reminder Added.");
|
||||||
|
hideAddReminderRecordModal();
|
||||||
|
getVehicleReminders(formValues.vehicleId);
|
||||||
|
} else {
|
||||||
|
errorToast("An error has occurred, please try again later.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function appendMileageToOdometer(increment) {
|
||||||
|
var reminderMileage = $("#reminderMileage").val();
|
||||||
|
var reminderMileageIsInvalid = reminderMileage.trim() == '' || parseInt(reminderMileage) < 0;
|
||||||
|
if (reminderMileageIsInvalid) {
|
||||||
|
reminderMileage = 0;
|
||||||
|
} else {
|
||||||
|
reminderMileage = parseInt(reminderMileage);
|
||||||
|
}
|
||||||
|
reminderMileage += increment;
|
||||||
|
$("#reminderMileage").val(reminderMileage);
|
||||||
|
}
|
||||||
|
function getAndValidateReminderRecordValues() {
|
||||||
|
var reminderDate = $("#reminderDate").val();
|
||||||
|
var reminderMileage = $("#reminderMileage").val();
|
||||||
|
var reminderDescription = $("#reminderDescription").val();
|
||||||
|
var reminderNotes = $("#reminderNotes").val();
|
||||||
|
var reminderOption = $('#reminderOptions input:radio:checked').val();
|
||||||
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
|
var reminderId = getReminderRecordModelData().id;
|
||||||
|
//validation
|
||||||
|
var hasError = false;
|
||||||
|
var reminderDateIsInvalid = reminderDate.trim() == ''; //eliminates whitespace.
|
||||||
|
var reminderMileageIsInvalid = reminderMileage.trim() == '' || parseInt(reminderMileage) < 0;
|
||||||
|
if ((reminderOption == "Both" || reminderOption == "Date") && reminderDateIsInvalid) {
|
||||||
|
hasError = true;
|
||||||
|
$("#reminderDate").addClass("is-invalid");
|
||||||
|
} else if (reminderOption == "Date") {
|
||||||
|
$("#reminderDate").removeClass("is-invalid");
|
||||||
|
}
|
||||||
|
if ((reminderOption == "Both" || reminderOption == "Odometer") && reminderMileageIsInvalid) {
|
||||||
|
hasError = true;
|
||||||
|
$("#reminderMileage").addClass("is-invalid");
|
||||||
|
} else if (reminderOption == "Odometer") {
|
||||||
|
$("#reminderMileage").removeClass("is-invalid");
|
||||||
|
}
|
||||||
|
if (reminderDescription.trim() == '') {
|
||||||
|
hasError = true;
|
||||||
|
$("#reminderDescription").addClass("is-invalid");
|
||||||
|
} else {
|
||||||
|
$("#reminderDescription").removeClass("is-invalid");
|
||||||
|
}
|
||||||
|
if (reminderOption == undefined) {
|
||||||
|
hasError = true;
|
||||||
|
$("#reminderMetricDate").addClass("is-invalid");
|
||||||
|
$("#reminderMetricOdometer").addClass("is-invalid");
|
||||||
|
$("#reminderMetricBoth").addClass("is-invalid");
|
||||||
|
} else {
|
||||||
|
$("#reminderMetricDate").removeClass("is-invalid");
|
||||||
|
$("#reminderMetricOdometer").removeClass("is-invalid");
|
||||||
|
$("#reminderMetricBoth").removeClass("is-invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: reminderId,
|
||||||
|
hasError: hasError,
|
||||||
|
vehicleId: vehicleId,
|
||||||
|
date: reminderDate,
|
||||||
|
mileage: reminderMileage,
|
||||||
|
description: reminderDescription,
|
||||||
|
notes: reminderNotes,
|
||||||
|
metric: reminderOption
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
$("#serviceRecordModalContent").html(data);
|
$("#serviceRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#serviceRecordDate').datepicker({
|
$('#serviceRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#serviceRecordModal').modal('show');
|
$('#serviceRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -16,7 +17,8 @@ function showEditServiceRecordModal(serviceRecordId) {
|
|||||||
$("#serviceRecordModalContent").html(data);
|
$("#serviceRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#serviceRecordDate').datepicker({
|
$('#serviceRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#serviceRecordModal').modal('show');
|
$('#serviceRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -64,6 +66,9 @@ function saveServiceRecordToVehicle(isEdit) {
|
|||||||
successToast(isEdit ? "Service Record Updated" : "Service Record Added.");
|
successToast(isEdit ? "Service Record Updated" : "Service Record Added.");
|
||||||
hideAddServiceRecordModal();
|
hideAddServiceRecordModal();
|
||||||
getVehicleServiceRecords(formValues.vehicleId);
|
getVehicleServiceRecords(formValues.vehicleId);
|
||||||
|
if (formValues.addReminderRecord) {
|
||||||
|
setTimeout(function () { showAddReminderModal(formValues); }, 500);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorToast("An error has occurred, please try again later.");
|
errorToast("An error has occurred, please try again later.");
|
||||||
}
|
}
|
||||||
@@ -77,6 +82,7 @@ function getAndValidateServiceRecordValues() {
|
|||||||
var serviceNotes = $("#serviceRecordNotes").val();
|
var serviceNotes = $("#serviceRecordNotes").val();
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var serviceRecordId = getServiceRecordModelData().id;
|
var serviceRecordId = getServiceRecordModelData().id;
|
||||||
|
var addReminderRecord = $("#addReminderCheck").is(":checked");
|
||||||
//validation
|
//validation
|
||||||
var hasError = false;
|
var hasError = false;
|
||||||
if (serviceDate.trim() == '') { //eliminates whitespace.
|
if (serviceDate.trim() == '') { //eliminates whitespace.
|
||||||
@@ -112,7 +118,8 @@ function getAndValidateServiceRecordValues() {
|
|||||||
description: serviceDescription,
|
description: serviceDescription,
|
||||||
cost: serviceCost,
|
cost: serviceCost,
|
||||||
notes: serviceNotes,
|
notes: serviceNotes,
|
||||||
files: uploadedFiles
|
files: uploadedFiles,
|
||||||
|
addReminderRecord: addReminderRecord
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function deleteServiceRecordFile(fileLocation, event) {
|
function deleteServiceRecordFile(fileLocation, event) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ function saveVehicle(isEdit) {
|
|||||||
var vehicleMake = $("#inputMake").val();
|
var vehicleMake = $("#inputMake").val();
|
||||||
var vehicleModel = $("#inputModel").val();
|
var vehicleModel = $("#inputModel").val();
|
||||||
var vehicleLicensePlate = $("#inputLicensePlate").val();
|
var vehicleLicensePlate = $("#inputLicensePlate").val();
|
||||||
|
var vehicleIsElectric = $("#inputIsElectric").is(":checked");
|
||||||
//validate
|
//validate
|
||||||
var hasError = false;
|
var hasError = false;
|
||||||
if (vehicleYear.trim() == '' || parseInt(vehicleYear) < 1900) {
|
if (vehicleYear.trim() == '' || parseInt(vehicleYear) < 1900) {
|
||||||
@@ -72,7 +73,8 @@ function saveVehicle(isEdit) {
|
|||||||
year: vehicleYear,
|
year: vehicleYear,
|
||||||
make: vehicleMake,
|
make: vehicleMake,
|
||||||
model: vehicleModel,
|
model: vehicleModel,
|
||||||
licensePlate: vehicleLicensePlate
|
licensePlate: vehicleLicensePlate,
|
||||||
|
isElectric: vehicleIsElectric
|
||||||
}, function (data) {
|
}, function (data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
if (!isEdit) {
|
if (!isEdit) {
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
$("#taxRecordModalContent").html(data);
|
$("#taxRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#taxRecordDate').datepicker({
|
$('#taxRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#taxRecordModal').modal('show');
|
$('#taxRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -16,7 +17,8 @@ function showEditTaxRecordModal(taxRecordId) {
|
|||||||
$("#taxRecordModalContent").html(data);
|
$("#taxRecordModalContent").html(data);
|
||||||
//initiate datepicker
|
//initiate datepicker
|
||||||
$('#taxRecordDate').datepicker({
|
$('#taxRecordDate').datepicker({
|
||||||
endDate: "+0d"
|
endDate: "+0d",
|
||||||
|
format: getShortDatePattern().pattern
|
||||||
});
|
});
|
||||||
$('#taxRecordModal').modal('show');
|
$('#taxRecordModal').modal('show');
|
||||||
}
|
}
|
||||||
@@ -64,6 +66,9 @@ function saveTaxRecordToVehicle(isEdit) {
|
|||||||
successToast(isEdit ? "Tax Record Updated" : "Tax Record Added.");
|
successToast(isEdit ? "Tax Record Updated" : "Tax Record Added.");
|
||||||
hideAddTaxRecordModal();
|
hideAddTaxRecordModal();
|
||||||
getVehicleTaxRecords(formValues.vehicleId);
|
getVehicleTaxRecords(formValues.vehicleId);
|
||||||
|
if (formValues.addReminderRecord) {
|
||||||
|
setTimeout(function () { showAddReminderModal(formValues); }, 500);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errorToast("An error has occurred, please try again later.");
|
errorToast("An error has occurred, please try again later.");
|
||||||
}
|
}
|
||||||
@@ -76,6 +81,7 @@ function getAndValidateTaxRecordValues() {
|
|||||||
var taxNotes = $("#taxRecordNotes").val();
|
var taxNotes = $("#taxRecordNotes").val();
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var taxRecordId = getTaxRecordModelData().id;
|
var taxRecordId = getTaxRecordModelData().id;
|
||||||
|
var addReminderRecord = $("#addReminderCheck").is(":checked");
|
||||||
//validation
|
//validation
|
||||||
var hasError = false;
|
var hasError = false;
|
||||||
if (taxDate.trim() == '') { //eliminates whitespace.
|
if (taxDate.trim() == '') { //eliminates whitespace.
|
||||||
@@ -104,7 +110,8 @@ function getAndValidateTaxRecordValues() {
|
|||||||
description: taxDescription,
|
description: taxDescription,
|
||||||
cost: taxCost,
|
cost: taxCost,
|
||||||
notes: taxNotes,
|
notes: taxNotes,
|
||||||
files: uploadedFiles
|
files: uploadedFiles,
|
||||||
|
addReminderRecord: addReminderRecord
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function deleteTaxRecordFile(fileLocation, event) {
|
function deleteTaxRecordFile(fileLocation, event) {
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ $(document).ready(function () {
|
|||||||
case "report-tab":
|
case "report-tab":
|
||||||
getVehicleReport();
|
getVehicleReport();
|
||||||
break;
|
break;
|
||||||
|
case "reminder-tab":
|
||||||
|
getVehicleReminders(vehicleId);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
switch (e.relatedTarget.id) { //clear out previous tabs with grids in them to help with performance
|
switch (e.relatedTarget.id) { //clear out previous tabs with grids in them to help with performance
|
||||||
case "servicerecord-tab":
|
case "servicerecord-tab":
|
||||||
@@ -51,6 +54,9 @@ $(document).ready(function () {
|
|||||||
case "report-tab":
|
case "report-tab":
|
||||||
$("#report-tab-pane").html("");
|
$("#report-tab-pane").html("");
|
||||||
break;
|
break;
|
||||||
|
case "reminder-tab":
|
||||||
|
$("#reminder-tab-pane").html("");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
getVehicleServiceRecords(vehicleId);
|
getVehicleServiceRecords(vehicleId);
|
||||||
@@ -67,6 +73,7 @@ function getVehicleServiceRecords(vehicleId) {
|
|||||||
$.get(`/Vehicle/GetServiceRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
$.get(`/Vehicle/GetServiceRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
$("#servicerecord-tab-pane").html(data);
|
$("#servicerecord-tab-pane").html(data);
|
||||||
|
getVehicleHaveImportantReminders(vehicleId);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -74,6 +81,7 @@ function getVehicleGasRecords(vehicleId) {
|
|||||||
$.get(`/Vehicle/GetGasRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
$.get(`/Vehicle/GetGasRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
$("#gas-tab-pane").html(data);
|
$("#gas-tab-pane").html(data);
|
||||||
|
getVehicleHaveImportantReminders(vehicleId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -81,6 +89,7 @@ function getVehicleCollisionRecords(vehicleId) {
|
|||||||
$.get(`/Vehicle/GetCollisionRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
$.get(`/Vehicle/GetCollisionRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
$("#accident-tab-pane").html(data);
|
$("#accident-tab-pane").html(data);
|
||||||
|
getVehicleHaveImportantReminders(vehicleId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -88,6 +97,15 @@ function getVehicleTaxRecords(vehicleId) {
|
|||||||
$.get(`/Vehicle/GetTaxRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
$.get(`/Vehicle/GetTaxRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
$("#tax-tab-pane").html(data);
|
$("#tax-tab-pane").html(data);
|
||||||
|
getVehicleHaveImportantReminders(vehicleId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getVehicleReminders(vehicleId) {
|
||||||
|
$.get(`/Vehicle/GetReminderRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
|
||||||
|
if (data) {
|
||||||
|
$("#reminder-tab-pane").html(data);
|
||||||
|
getVehicleHaveImportantReminders(vehicleId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -158,4 +176,40 @@ function uploadVehicleFilesAsync(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
function showAddReminderModal(reminderModalInput) {
|
||||||
|
if (reminderModalInput != undefined) {
|
||||||
|
$.post('/Vehicle/GetAddReminderRecordPartialView', {reminderModel: reminderModalInput}, function (data) {
|
||||||
|
$("#reminderRecordModalContent").html(data);
|
||||||
|
$('#reminderDate').datepicker({
|
||||||
|
startDate: "+0d"
|
||||||
|
});
|
||||||
|
$("#reminderRecordModal").modal("show");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$.post('/Vehicle/GetAddReminderRecordPartialView', function (data) {
|
||||||
|
$("#reminderRecordModalContent").html(data);
|
||||||
|
$('#reminderDate').datepicker({
|
||||||
|
startDate: "+0d"
|
||||||
|
});
|
||||||
|
$("#reminderRecordModal").modal("show");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getVehicleHaveImportantReminders(vehicleId) {
|
||||||
|
setTimeout(function () {
|
||||||
|
$.get(`/Vehicle/GetVehicleHaveUrgentOrPastDueReminders?vehicleId=${vehicleId}`, function (data) {
|
||||||
|
if (data) {
|
||||||
|
$("#reminderBell").removeClass("bi-bell");
|
||||||
|
$("#reminderBell").addClass("bi-bell-fill");
|
||||||
|
$("#reminderBell").addClass("text-warning");
|
||||||
|
$("#reminderBellDiv").addClass("bell-shake");
|
||||||
|
} else {
|
||||||
|
$("#reminderBellDiv").removeClass("bell-shake");
|
||||||
|
$("#reminderBell").removeClass("bi-bell-fill");
|
||||||
|
$("#reminderBell").addClass("bi-bell");
|
||||||
|
$("#reminderBell").removeClass("text-warning");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user