Compare commits
36 Commits
v1.4.6
...
Hargata/up
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29680cf0e9 | ||
|
|
723eb1a769 | ||
|
|
732a628c20 | ||
|
|
84d40edb0e | ||
|
|
9842f0e501 | ||
|
|
44c5f921a5 | ||
|
|
fae4aa31aa | ||
|
|
adb505c87c | ||
|
|
d21b0a9e29 | ||
|
|
15e4181aff | ||
|
|
3a4627ef02 | ||
|
|
d9e11273bd | ||
|
|
8e6f40a1b1 | ||
|
|
55f86ecb3f | ||
|
|
8eef1465cf | ||
|
|
fd0ffba7c4 | ||
|
|
afb710e6ad | ||
|
|
419b755e7a | ||
|
|
8cd5342c02 | ||
|
|
fa32ecdb5a | ||
|
|
d08726bd85 | ||
|
|
a87861069b | ||
|
|
b6d6a8765d | ||
|
|
f307c0933a | ||
|
|
9968ccb541 | ||
|
|
5d3746f168 | ||
|
|
df0c4eeca2 | ||
|
|
5b3c0aed72 | ||
|
|
040728b96d | ||
|
|
e8b7b3e4ba | ||
|
|
cf1f1a884b | ||
|
|
c470db0590 | ||
|
|
cb71650adf | ||
|
|
923d59af0a | ||
|
|
d68e1a3939 | ||
|
|
ffb126276f |
@@ -112,6 +112,34 @@ namespace CarCareTracker.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[Route("/api/version")]
|
||||||
|
public async Task<IActionResult> ServerVersion(bool checkForUpdate = false)
|
||||||
|
{
|
||||||
|
var viewModel = new ReleaseVersion
|
||||||
|
{
|
||||||
|
CurrentVersion = StaticHelper.VersionNumber,
|
||||||
|
LatestVersion = StaticHelper.VersionNumber
|
||||||
|
};
|
||||||
|
if (checkForUpdate)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("request");
|
||||||
|
var releaseResponse = await httpClient.GetFromJsonAsync<ReleaseResponse>(StaticHelper.ReleasePath) ?? new ReleaseResponse();
|
||||||
|
if (!string.IsNullOrWhiteSpace(releaseResponse.tag_name))
|
||||||
|
{
|
||||||
|
viewModel.LatestVersion = releaseResponse.tag_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed($"Unable to retrieve latest version from GitHub API: {ex.Message}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Json(viewModel);
|
||||||
|
}
|
||||||
|
[HttpGet]
|
||||||
[Route("/api/vehicles")]
|
[Route("/api/vehicles")]
|
||||||
public IActionResult Vehicles()
|
public IActionResult Vehicles()
|
||||||
{
|
{
|
||||||
@@ -256,6 +284,15 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Progress cannot be set to Done."));
|
return Json(OperationResponse.Failed("Input object invalid, Progress cannot be set to Done."));
|
||||||
}
|
}
|
||||||
|
//hardening - turns null values for List types into empty lists.
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var planRecord = new PlanRecord()
|
var planRecord = new PlanRecord()
|
||||||
@@ -346,6 +383,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Progress cannot be set to Done."));
|
return Json(OperationResponse.Failed("Input object invalid, Progress cannot be set to Done."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -429,6 +474,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Date, Description, Odometer, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Date, Description, Odometer, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var serviceRecord = new ServiceRecord()
|
var serviceRecord = new ServiceRecord()
|
||||||
@@ -509,6 +562,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, Odometer, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, Odometer, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -591,6 +652,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Date, Description, Odometer, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Date, Description, Odometer, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var repairRecord = new CollisionRecord()
|
var repairRecord = new CollisionRecord()
|
||||||
@@ -672,6 +741,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, Odometer, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, Odometer, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -755,6 +832,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Date, Description, Odometer, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Date, Description, Odometer, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var upgradeRecord = new UpgradeRecord()
|
var upgradeRecord = new UpgradeRecord()
|
||||||
@@ -835,6 +920,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, Odometer, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, Odometer, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -951,6 +1044,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Date, Description, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Date, Description, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var taxRecord = new TaxRecord()
|
var taxRecord = new TaxRecord()
|
||||||
@@ -1014,6 +1115,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Description, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -1113,6 +1222,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Date, and Odometer cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Date, and Odometer cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var odometerRecord = new OdometerRecord()
|
var odometerRecord = new OdometerRecord()
|
||||||
@@ -1174,6 +1291,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Initial Odometer, and Odometer cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Initial Odometer, and Odometer cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -1273,6 +1398,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Date, Odometer, FuelConsumed, IsFillToFull, MissedFuelUp, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Date, Odometer, FuelConsumed, IsFillToFull, MissedFuelUp, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var gasRecord = new GasRecord()
|
var gasRecord = new GasRecord()
|
||||||
@@ -1352,6 +1485,14 @@ namespace CarCareTracker.Controllers
|
|||||||
Response.StatusCode = 400;
|
Response.StatusCode = 400;
|
||||||
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Odometer, FuelConsumed, IsFillToFull, MissedFuelUp, and Cost cannot be empty."));
|
return Json(OperationResponse.Failed("Input object invalid, Id, Date, Odometer, FuelConsumed, IsFillToFull, MissedFuelUp, and Cost cannot be empty."));
|
||||||
}
|
}
|
||||||
|
if (input.Files == null)
|
||||||
|
{
|
||||||
|
input.Files = new List<UploadedFiles>();
|
||||||
|
}
|
||||||
|
if (input.ExtraFields == null)
|
||||||
|
{
|
||||||
|
input.ExtraFields = new List<ExtraField>();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//retrieve existing record
|
//retrieve existing record
|
||||||
@@ -1391,6 +1532,7 @@ namespace CarCareTracker.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
#region ReminderRecord
|
||||||
[TypeFilter(typeof(CollaboratorFilter))]
|
[TypeFilter(typeof(CollaboratorFilter))]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("/api/vehicle/reminders")]
|
[Route("/api/vehicle/reminders")]
|
||||||
@@ -1403,7 +1545,7 @@ namespace CarCareTracker.Controllers
|
|||||||
}
|
}
|
||||||
var currentMileage = _vehicleLogic.GetMaxMileage(vehicleId);
|
var currentMileage = _vehicleLogic.GetMaxMileage(vehicleId);
|
||||||
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicleId);
|
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicleId);
|
||||||
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now).Select(x=> new ReminderExportModel { Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString()});
|
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now).Select(x=> new ReminderExportModel { Id = x.Id.ToString(), Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString(), Tags = string.Join(' ', x.Tags) });
|
||||||
if (_config.GetInvariantApi() || Request.Headers.ContainsKey("culture-invariant"))
|
if (_config.GetInvariantApi() || Request.Headers.ContainsKey("culture-invariant"))
|
||||||
{
|
{
|
||||||
return Json(results, StaticHelper.GetInvariantOption());
|
return Json(results, StaticHelper.GetInvariantOption());
|
||||||
@@ -1413,6 +1555,183 @@ namespace CarCareTracker.Controllers
|
|||||||
return Json(results);
|
return Json(results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[TypeFilter(typeof(CollaboratorFilter))]
|
||||||
|
[HttpPost]
|
||||||
|
[Route("/api/vehicle/reminders/add")]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
public IActionResult AddReminderRecordJson(int vehicleId, [FromBody] ReminderExportModel input) => AddReminderRecord(vehicleId, input);
|
||||||
|
[TypeFilter(typeof(CollaboratorFilter))]
|
||||||
|
[HttpPost]
|
||||||
|
[Route("/api/vehicle/reminders/add")]
|
||||||
|
public IActionResult AddReminderRecord(int vehicleId, ReminderExportModel input)
|
||||||
|
{
|
||||||
|
if (vehicleId == default)
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Must provide a valid vehicle id"));
|
||||||
|
}
|
||||||
|
if (string.IsNullOrWhiteSpace(input.Description) ||
|
||||||
|
string.IsNullOrWhiteSpace(input.Metric))
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, Description and Metric cannot be empty."));
|
||||||
|
}
|
||||||
|
bool validMetric = Enum.TryParse(input.Metric, out ReminderMetric parsedMetric);
|
||||||
|
bool validDate = DateTime.TryParse(input.DueDate, out DateTime parsedDate);
|
||||||
|
bool validOdometer = int.TryParse(input.DueOdometer, out int parsedOdometer);
|
||||||
|
if (!validMetric)
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, values for Metric(Date, Odometer, Both) is invalid."));
|
||||||
|
}
|
||||||
|
//validate metrics
|
||||||
|
switch (parsedMetric)
|
||||||
|
{
|
||||||
|
case ReminderMetric.Both:
|
||||||
|
//validate due date and odometer
|
||||||
|
if (!validDate || !validOdometer)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, DueDate and DueOdometer must be valid if Metric is Both"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReminderMetric.Date:
|
||||||
|
if (!validDate)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, DueDate must be valid if Metric is Date"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReminderMetric.Odometer:
|
||||||
|
if (!validOdometer)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, DueOdometer must be valid if Metric is Odometer"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var reminderRecord = new ReminderRecord()
|
||||||
|
{
|
||||||
|
VehicleId = vehicleId,
|
||||||
|
Description = input.Description,
|
||||||
|
Mileage = parsedOdometer,
|
||||||
|
Date = parsedDate,
|
||||||
|
Metric = parsedMetric,
|
||||||
|
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
|
||||||
|
Tags = string.IsNullOrWhiteSpace(input.Tags) ? new List<string>() : input.Tags.Split(' ').Distinct().ToList()
|
||||||
|
};
|
||||||
|
_reminderRecordDataAccess.SaveReminderRecordToVehicle(reminderRecord);
|
||||||
|
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromReminderRecord(reminderRecord, "reminderrecord.add.api", User.Identity.Name));
|
||||||
|
return Json(OperationResponse.Succeed("Reminder Record Added"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Response.StatusCode = 500;
|
||||||
|
return Json(OperationResponse.Failed(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[HttpPut]
|
||||||
|
[Route("/api/vehicle/reminders/update")]
|
||||||
|
[Consumes("application/json")]
|
||||||
|
public IActionResult UpdateReminderRecordJson([FromBody] ReminderExportModel input) => UpdateReminderRecord(input);
|
||||||
|
[HttpPut]
|
||||||
|
[Route("/api/vehicle/reminders/update")]
|
||||||
|
public IActionResult UpdateReminderRecord(ReminderExportModel input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input.Id) ||
|
||||||
|
string.IsNullOrWhiteSpace(input.Description) ||
|
||||||
|
string.IsNullOrWhiteSpace(input.Metric))
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, Id, Description and Metric cannot be empty."));
|
||||||
|
}
|
||||||
|
bool validMetric = Enum.TryParse(input.Metric, out ReminderMetric parsedMetric);
|
||||||
|
bool validDate = DateTime.TryParse(input.DueDate, out DateTime parsedDate);
|
||||||
|
bool validOdometer = int.TryParse(input.DueOdometer, out int parsedOdometer);
|
||||||
|
if (!validMetric)
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, values for Metric(Date, Odometer, Both) is invalid."));
|
||||||
|
}
|
||||||
|
//validate metrics
|
||||||
|
switch (parsedMetric)
|
||||||
|
{
|
||||||
|
case ReminderMetric.Both:
|
||||||
|
//validate due date and odometer
|
||||||
|
if (!validDate || !validOdometer)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, DueDate and DueOdometer must be valid if Metric is Both"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReminderMetric.Date:
|
||||||
|
if (!validDate)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, DueDate must be valid if Metric is Date"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ReminderMetric.Odometer:
|
||||||
|
if (!validOdometer)
|
||||||
|
{
|
||||||
|
return Json(OperationResponse.Failed("Input object invalid, DueOdometer must be valid if Metric is Odometer"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//retrieve existing record
|
||||||
|
var existingRecord = _reminderRecordDataAccess.GetReminderRecordById(int.Parse(input.Id));
|
||||||
|
if (existingRecord != null && existingRecord.Id == int.Parse(input.Id))
|
||||||
|
{
|
||||||
|
//check if user has access to the vehicleId
|
||||||
|
if (!_userLogic.UserCanEditVehicle(GetUserID(), existingRecord.VehicleId))
|
||||||
|
{
|
||||||
|
Response.StatusCode = 401;
|
||||||
|
return Json(OperationResponse.Failed("Access Denied, you don't have access to this vehicle."));
|
||||||
|
}
|
||||||
|
existingRecord.Date = parsedDate;
|
||||||
|
existingRecord.Mileage = parsedOdometer;
|
||||||
|
existingRecord.Description = input.Description;
|
||||||
|
existingRecord.Metric = parsedMetric;
|
||||||
|
existingRecord.Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes;
|
||||||
|
existingRecord.Tags = string.IsNullOrWhiteSpace(input.Tags) ? new List<string>() : input.Tags.Split(' ').Distinct().ToList();
|
||||||
|
_reminderRecordDataAccess.SaveReminderRecordToVehicle(existingRecord);
|
||||||
|
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromReminderRecord(existingRecord, "reminderrecord.update.api", User.Identity.Name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Invalid Record Id"));
|
||||||
|
}
|
||||||
|
return Json(OperationResponse.Succeed("Reminder Record Updated"));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Response.StatusCode = 500;
|
||||||
|
return Json(OperationResponse.Failed(ex.Message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("/api/vehicle/reminders/delete")]
|
||||||
|
public IActionResult DeleteReminderRecord(int id)
|
||||||
|
{
|
||||||
|
var existingRecord = _reminderRecordDataAccess.GetReminderRecordById(id);
|
||||||
|
if (existingRecord == null || existingRecord.Id == default)
|
||||||
|
{
|
||||||
|
Response.StatusCode = 400;
|
||||||
|
return Json(OperationResponse.Failed("Invalid Record Id"));
|
||||||
|
}
|
||||||
|
//security check.
|
||||||
|
if (!_userLogic.UserCanEditVehicle(GetUserID(), existingRecord.VehicleId))
|
||||||
|
{
|
||||||
|
Response.StatusCode = 401;
|
||||||
|
return Json(OperationResponse.Failed("Access Denied, you don't have access to this vehicle."));
|
||||||
|
}
|
||||||
|
var result = _reminderRecordDataAccess.DeleteReminderRecordById(existingRecord.Id);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromReminderRecord(existingRecord, "reminderrecord.delete.api", User.Identity.Name));
|
||||||
|
}
|
||||||
|
return Json(OperationResponse.Conditional(result, "Reminder Record Deleted"));
|
||||||
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("/api/calendar")]
|
[Route("/api/calendar")]
|
||||||
public IActionResult Calendar()
|
public IActionResult Calendar()
|
||||||
@@ -1426,6 +1745,7 @@ namespace CarCareTracker.Controllers
|
|||||||
var calendarContent = StaticHelper.RemindersToCalendar(reminders);
|
var calendarContent = StaticHelper.RemindersToCalendar(reminders);
|
||||||
return File(calendarContent, "text/calendar");
|
return File(calendarContent, "text/calendar");
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("/api/documents/upload")]
|
[Route("/api/documents/upload")]
|
||||||
public IActionResult UploadDocument(List<IFormFile> documents)
|
public IActionResult UploadDocument(List<IFormFile> documents)
|
||||||
|
|||||||
@@ -49,21 +49,31 @@ namespace CarCareTracker.Controllers
|
|||||||
}
|
}
|
||||||
return View(model: redirectURL);
|
return View(model: redirectURL);
|
||||||
}
|
}
|
||||||
public IActionResult Registration()
|
public IActionResult Registration(string token = "", string email = "")
|
||||||
{
|
{
|
||||||
if (_config.GetServerDisabledRegistration())
|
if (_config.GetServerDisabledRegistration())
|
||||||
{
|
{
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
return View();
|
var viewModel = new LoginModel
|
||||||
|
{
|
||||||
|
EmailAddress = string.IsNullOrWhiteSpace(email) ? string.Empty : email,
|
||||||
|
Token = string.IsNullOrWhiteSpace(token) ? string.Empty : token
|
||||||
|
};
|
||||||
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
public IActionResult ForgotPassword()
|
public IActionResult ForgotPassword()
|
||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
public IActionResult ResetPassword()
|
public IActionResult ResetPassword(string token = "", string email = "")
|
||||||
{
|
{
|
||||||
return View();
|
var viewModel = new LoginModel
|
||||||
|
{
|
||||||
|
EmailAddress = string.IsNullOrWhiteSpace(email) ? string.Empty : email,
|
||||||
|
Token = string.IsNullOrWhiteSpace(token) ? string.Empty : token
|
||||||
|
};
|
||||||
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
public IActionResult GetRemoteLoginLink()
|
public IActionResult GetRemoteLoginLink()
|
||||||
{
|
{
|
||||||
@@ -130,7 +140,9 @@ namespace CarCareTracker.Controllers
|
|||||||
Content = new FormUrlEncodedContent(httpParams)
|
Content = new FormUrlEncodedContent(httpParams)
|
||||||
};
|
};
|
||||||
var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync();
|
var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync();
|
||||||
var userJwt = JsonSerializer.Deserialize<OpenIDResult>(tokenResult)?.id_token ?? string.Empty;
|
var decodedToken = JsonSerializer.Deserialize<OpenIDResult>(tokenResult);
|
||||||
|
var userJwt = decodedToken?.id_token ?? string.Empty;
|
||||||
|
var userAccessToken = decodedToken?.access_token ?? string.Empty;
|
||||||
if (!string.IsNullOrWhiteSpace(userJwt))
|
if (!string.IsNullOrWhiteSpace(userJwt))
|
||||||
{
|
{
|
||||||
//validate JWT token
|
//validate JWT token
|
||||||
@@ -140,7 +152,23 @@ namespace CarCareTracker.Controllers
|
|||||||
if (parsedToken.Claims.Any(x => x.Type == "email"))
|
if (parsedToken.Claims.Any(x => x.Type == "email"))
|
||||||
{
|
{
|
||||||
userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
|
userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
|
||||||
} else
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(openIdConfig.UserInfoURL) && !string.IsNullOrWhiteSpace(userAccessToken))
|
||||||
|
{
|
||||||
|
//retrieve claims from userinfo endpoint if no email claims are returned within id_token
|
||||||
|
var userInfoHttpRequest = new HttpRequestMessage(HttpMethod.Get, openIdConfig.UserInfoURL);
|
||||||
|
userInfoHttpRequest.Headers.Add("Authorization", $"Bearer {userAccessToken}");
|
||||||
|
var userInfoResult = await httpClient.SendAsync(userInfoHttpRequest).Result.Content.ReadAsStringAsync();
|
||||||
|
var userInfo = JsonSerializer.Deserialize<OpenIDUserInfo>(userInfoResult);
|
||||||
|
if (!string.IsNullOrWhiteSpace(userInfo?.email ?? string.Empty))
|
||||||
|
{
|
||||||
|
userEmailAddress = userInfo?.email ?? string.Empty;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
_logger.LogError($"OpenID Provider did not provide an email claim via UserInfo endpoint");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var returnedClaims = parsedToken.Claims.Select(x => x.Type);
|
var returnedClaims = parsedToken.Claims.Select(x => x.Type);
|
||||||
_logger.LogError($"OpenID Provider did not provide an email claim, claims returned: {string.Join(",", returnedClaims)}");
|
_logger.LogError($"OpenID Provider did not provide an email claim, claims returned: {string.Join(",", returnedClaims)}");
|
||||||
@@ -239,7 +267,9 @@ namespace CarCareTracker.Controllers
|
|||||||
Content = new FormUrlEncodedContent(httpParams)
|
Content = new FormUrlEncodedContent(httpParams)
|
||||||
};
|
};
|
||||||
var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync();
|
var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync();
|
||||||
var userJwt = JsonSerializer.Deserialize<OpenIDResult>(tokenResult)?.id_token ?? string.Empty;
|
var decodedToken = JsonSerializer.Deserialize<OpenIDResult>(tokenResult);
|
||||||
|
var userJwt = decodedToken?.id_token ?? string.Empty;
|
||||||
|
var userAccessToken = decodedToken?.access_token ?? string.Empty;
|
||||||
if (!string.IsNullOrWhiteSpace(userJwt))
|
if (!string.IsNullOrWhiteSpace(userJwt))
|
||||||
{
|
{
|
||||||
results.Add(OperationResponse.Succeed($"Passed JWT Parsing - id_token: {userJwt}"));
|
results.Add(OperationResponse.Succeed($"Passed JWT Parsing - id_token: {userJwt}"));
|
||||||
@@ -252,6 +282,22 @@ namespace CarCareTracker.Controllers
|
|||||||
userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
|
userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
|
||||||
results.Add(OperationResponse.Succeed($"Passed Claim Validation - email"));
|
results.Add(OperationResponse.Succeed($"Passed Claim Validation - email"));
|
||||||
}
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(openIdConfig.UserInfoURL) && !string.IsNullOrWhiteSpace(userAccessToken))
|
||||||
|
{
|
||||||
|
//retrieve claims from userinfo endpoint if no email claims are returned within id_token
|
||||||
|
var userInfoHttpRequest = new HttpRequestMessage(HttpMethod.Get, openIdConfig.UserInfoURL);
|
||||||
|
userInfoHttpRequest.Headers.Add("Authorization", $"Bearer {userAccessToken}");
|
||||||
|
var userInfoResult = await httpClient.SendAsync(userInfoHttpRequest).Result.Content.ReadAsStringAsync();
|
||||||
|
var userInfo = JsonSerializer.Deserialize<OpenIDUserInfo>(userInfoResult);
|
||||||
|
if (!string.IsNullOrWhiteSpace(userInfo?.email ?? string.Empty))
|
||||||
|
{
|
||||||
|
userEmailAddress = userInfo?.email ?? string.Empty;
|
||||||
|
results.Add(OperationResponse.Succeed($"Passed Claim Validation - Retrieved email via UserInfo endpoint"));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
results.Add(OperationResponse.Failed($"Failed Claim Validation - Unable to retrieve email via UserInfo endpoint: {openIdConfig.UserInfoURL} using access_token: {userAccessToken} - Received {userInfoResult}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var returnedClaims = parsedToken.Claims.Select(x => x.Type);
|
var returnedClaims = parsedToken.Claims.Select(x => x.Type);
|
||||||
|
|||||||
@@ -14,14 +14,15 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
//get records
|
//get records
|
||||||
var vehicleData = _dataAccess.GetVehicleById(vehicleId);
|
var vehicleData = _dataAccess.GetVehicleById(vehicleId);
|
||||||
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
|
var vehicleRecords = _vehicleLogic.GetVehicleRecords(vehicleId);
|
||||||
var gasRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
var serviceRecords = vehicleRecords.ServiceRecords;
|
||||||
var collisionRecords = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId);
|
var gasRecords = vehicleRecords.GasRecords;
|
||||||
var taxRecords = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId);
|
var collisionRecords = vehicleRecords.CollisionRecords;
|
||||||
var upgradeRecords = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId);
|
var taxRecords = vehicleRecords.TaxRecords;
|
||||||
var odometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
|
var upgradeRecords = vehicleRecords.UpgradeRecords;
|
||||||
|
var odometerRecords = vehicleRecords.OdometerRecords;
|
||||||
var userConfig = _config.GetUserConfig(User);
|
var userConfig = _config.GetUserConfig(User);
|
||||||
var viewModel = new ReportViewModel();
|
var viewModel = new ReportViewModel() { ReportHeaderForVehicle = new ReportHeader() };
|
||||||
//check if custom widgets are configured
|
//check if custom widgets are configured
|
||||||
viewModel.CustomWidgetsConfigured = _fileHelper.WidgetsExist();
|
viewModel.CustomWidgetsConfigured = _fileHelper.WidgetsExist();
|
||||||
//get totalCostMakeUp
|
//get totalCostMakeUp
|
||||||
@@ -91,6 +92,7 @@ namespace CarCareTracker.Controllers
|
|||||||
var mileageData = _gasHelper.GetGasRecordViewModels(gasRecords, userConfig.UseMPG, userConfig.UseUKMPG);
|
var mileageData = _gasHelper.GetGasRecordViewModels(gasRecords, userConfig.UseMPG, userConfig.UseUKMPG);
|
||||||
string preferredFuelMileageUnit = _config.GetUserConfig(User).PreferredGasMileageUnit;
|
string preferredFuelMileageUnit = _config.GetUserConfig(User).PreferredGasMileageUnit;
|
||||||
var fuelEconomyMileageUnit = StaticHelper.GetFuelEconomyUnit(vehicleData.IsElectric, vehicleData.UseHours, userConfig.UseMPG, userConfig.UseUKMPG);
|
var fuelEconomyMileageUnit = StaticHelper.GetFuelEconomyUnit(vehicleData.IsElectric, vehicleData.UseHours, userConfig.UseMPG, userConfig.UseUKMPG);
|
||||||
|
var averageMPG = _gasHelper.GetAverageGasMileage(mileageData, userConfig.UseMPG);
|
||||||
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
||||||
bool invertedFuelMileageUnit = fuelEconomyMileageUnit == "l/100km" && preferredFuelMileageUnit == "km/l";
|
bool invertedFuelMileageUnit = fuelEconomyMileageUnit == "l/100km" && preferredFuelMileageUnit == "km/l";
|
||||||
var monthlyMileageData = StaticHelper.GetBaseLineCostsNoMonthName();
|
var monthlyMileageData = StaticHelper.GetBaseLineCostsNoMonthName();
|
||||||
@@ -114,6 +116,12 @@ namespace CarCareTracker.Controllers
|
|||||||
monthMileage.Cost = 100 / monthMileage.Cost;
|
monthMileage.Cost = 100 / monthMileage.Cost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var newAverageMPG = decimal.Parse(averageMPG, NumberStyles.Any);
|
||||||
|
if (newAverageMPG != 0)
|
||||||
|
{
|
||||||
|
newAverageMPG = 100 / newAverageMPG;
|
||||||
|
}
|
||||||
|
averageMPG = newAverageMPG.ToString("F");
|
||||||
}
|
}
|
||||||
var mpgViewModel = new MPGForVehicleByMonth {
|
var mpgViewModel = new MPGForVehicleByMonth {
|
||||||
CostData = monthlyMileageData,
|
CostData = monthlyMileageData,
|
||||||
@@ -121,6 +129,15 @@ namespace CarCareTracker.Controllers
|
|||||||
SortedCostData = (userConfig.UseMPG || invertedFuelMileageUnit) ? monthlyMileageData.OrderByDescending(x => x.Cost).ToList() : monthlyMileageData.OrderBy(x => x.Cost).ToList()
|
SortedCostData = (userConfig.UseMPG || invertedFuelMileageUnit) ? monthlyMileageData.OrderByDescending(x => x.Cost).ToList() : monthlyMileageData.OrderBy(x => x.Cost).ToList()
|
||||||
};
|
};
|
||||||
viewModel.FuelMileageForVehicleByMonth = mpgViewModel;
|
viewModel.FuelMileageForVehicleByMonth = mpgViewModel;
|
||||||
|
//report header
|
||||||
|
|
||||||
|
var maxMileage = _vehicleLogic.GetMaxMileage(vehicleRecords);
|
||||||
|
var minMileage = _vehicleLogic.GetMinMileage(vehicleRecords);
|
||||||
|
|
||||||
|
viewModel.ReportHeaderForVehicle.TotalCost = _vehicleLogic.GetVehicleTotalCost(vehicleRecords);
|
||||||
|
viewModel.ReportHeaderForVehicle.AverageMPG = $"{averageMPG} {mpgViewModel.Unit}";
|
||||||
|
viewModel.ReportHeaderForVehicle.MaxOdometer = maxMileage;
|
||||||
|
viewModel.ReportHeaderForVehicle.DistanceTraveled = maxMileage - minMileage;
|
||||||
return PartialView("_Report", viewModel);
|
return PartialView("_Report", viewModel);
|
||||||
}
|
}
|
||||||
[TypeFilter(typeof(CollaboratorFilter))]
|
[TypeFilter(typeof(CollaboratorFilter))]
|
||||||
@@ -145,6 +162,63 @@ namespace CarCareTracker.Controllers
|
|||||||
return Json(result);
|
return Json(result);
|
||||||
}
|
}
|
||||||
[TypeFilter(typeof(CollaboratorFilter))]
|
[TypeFilter(typeof(CollaboratorFilter))]
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult GetSummaryForVehicle(int vehicleId, int year = 0)
|
||||||
|
{
|
||||||
|
var vehicleData = _dataAccess.GetVehicleById(vehicleId);
|
||||||
|
var vehicleRecords = _vehicleLogic.GetVehicleRecords(vehicleId);
|
||||||
|
|
||||||
|
var serviceRecords = vehicleRecords.ServiceRecords;
|
||||||
|
var gasRecords = vehicleRecords.GasRecords;
|
||||||
|
var collisionRecords = vehicleRecords.CollisionRecords;
|
||||||
|
var taxRecords = vehicleRecords.TaxRecords;
|
||||||
|
var upgradeRecords = vehicleRecords.UpgradeRecords;
|
||||||
|
var odometerRecords = vehicleRecords.OdometerRecords;
|
||||||
|
|
||||||
|
if (year != default)
|
||||||
|
{
|
||||||
|
serviceRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
|
gasRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
|
collisionRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
|
taxRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
|
upgradeRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
|
odometerRecords.RemoveAll(x => x.Date.Year != year);
|
||||||
|
}
|
||||||
|
|
||||||
|
var userConfig = _config.GetUserConfig(User);
|
||||||
|
|
||||||
|
var mileageData = _gasHelper.GetGasRecordViewModels(gasRecords, userConfig.UseMPG, userConfig.UseUKMPG);
|
||||||
|
string preferredFuelMileageUnit = _config.GetUserConfig(User).PreferredGasMileageUnit;
|
||||||
|
var fuelEconomyMileageUnit = StaticHelper.GetFuelEconomyUnit(vehicleData.IsElectric, vehicleData.UseHours, userConfig.UseMPG, userConfig.UseUKMPG);
|
||||||
|
var averageMPG = _gasHelper.GetAverageGasMileage(mileageData, userConfig.UseMPG);
|
||||||
|
bool invertedFuelMileageUnit = fuelEconomyMileageUnit == "l/100km" && preferredFuelMileageUnit == "km/l";
|
||||||
|
|
||||||
|
if (invertedFuelMileageUnit)
|
||||||
|
{
|
||||||
|
var newAverageMPG = decimal.Parse(averageMPG, NumberStyles.Any);
|
||||||
|
if (newAverageMPG != 0)
|
||||||
|
{
|
||||||
|
newAverageMPG = 100 / newAverageMPG;
|
||||||
|
}
|
||||||
|
averageMPG = newAverageMPG.ToString("F");
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpgUnit = invertedFuelMileageUnit ? preferredFuelMileageUnit : fuelEconomyMileageUnit;
|
||||||
|
|
||||||
|
var maxMileage = _vehicleLogic.GetMaxMileage(vehicleRecords);
|
||||||
|
var minMileage = _vehicleLogic.GetMinMileage(vehicleRecords);
|
||||||
|
|
||||||
|
var viewModel = new ReportHeader()
|
||||||
|
{
|
||||||
|
TotalCost = _vehicleLogic.GetVehicleTotalCost(vehicleRecords),
|
||||||
|
AverageMPG = $"{averageMPG} {mpgUnit}",
|
||||||
|
MaxOdometer = maxMileage,
|
||||||
|
DistanceTraveled = maxMileage - minMileage
|
||||||
|
};
|
||||||
|
|
||||||
|
return PartialView("_ReportHeader", viewModel);
|
||||||
|
}
|
||||||
|
[TypeFilter(typeof(CollaboratorFilter))]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult GetCostMakeUpForVehicle(int vehicleId, int year = 0)
|
public IActionResult GetCostMakeUpForVehicle(int vehicleId, int year = 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,11 +19,13 @@ namespace CarCareTracker.Helper
|
|||||||
bool GetCustomWidgetsEnabled();
|
bool GetCustomWidgetsEnabled();
|
||||||
string GetMOTD();
|
string GetMOTD();
|
||||||
string GetLogoUrl();
|
string GetLogoUrl();
|
||||||
|
string GetSmallLogoUrl();
|
||||||
string GetServerLanguage();
|
string GetServerLanguage();
|
||||||
bool GetServerDisabledRegistration();
|
bool GetServerDisabledRegistration();
|
||||||
bool GetServerEnableShopSupplies();
|
bool GetServerEnableShopSupplies();
|
||||||
string GetServerPostgresConnection();
|
string GetServerPostgresConnection();
|
||||||
string GetAllowedFileUploadExtensions();
|
string GetAllowedFileUploadExtensions();
|
||||||
|
string GetServerDomain();
|
||||||
bool DeleteUserConfig(int userId);
|
bool DeleteUserConfig(int userId);
|
||||||
bool GetInvariantApi();
|
bool GetInvariantApi();
|
||||||
bool GetServerOpenRegistration();
|
bool GetServerOpenRegistration();
|
||||||
@@ -62,6 +64,11 @@ namespace CarCareTracker.Helper
|
|||||||
var motd = CheckString("LUBELOGGER_MOTD");
|
var motd = CheckString("LUBELOGGER_MOTD");
|
||||||
return motd;
|
return motd;
|
||||||
}
|
}
|
||||||
|
public string GetServerDomain()
|
||||||
|
{
|
||||||
|
var domain = CheckString("LUBELOGGER_DOMAIN");
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
public bool GetServerOpenRegistration()
|
public bool GetServerOpenRegistration()
|
||||||
{
|
{
|
||||||
return CheckBool(CheckString("LUBELOGGER_OPEN_REGISTRATION"));
|
return CheckBool(CheckString("LUBELOGGER_OPEN_REGISTRATION"));
|
||||||
@@ -86,6 +93,11 @@ namespace CarCareTracker.Helper
|
|||||||
var logoUrl = CheckString("LUBELOGGER_LOGO_URL", "/defaults/lubelogger_logo.png");
|
var logoUrl = CheckString("LUBELOGGER_LOGO_URL", "/defaults/lubelogger_logo.png");
|
||||||
return logoUrl;
|
return logoUrl;
|
||||||
}
|
}
|
||||||
|
public string GetSmallLogoUrl()
|
||||||
|
{
|
||||||
|
var logoUrl = CheckString("LUBELOGGER_LOGO_SMALL_URL", "/defaults/lubelogger_logo_small.png");
|
||||||
|
return logoUrl;
|
||||||
|
}
|
||||||
public string GetAllowedFileUploadExtensions()
|
public string GetAllowedFileUploadExtensions()
|
||||||
{
|
{
|
||||||
var allowedFileExtensions = CheckString("LUBELOGGER_ALLOWED_FILE_EXTENSIONS", StaticHelper.DefaultAllowedFileExtensions);
|
var allowedFileExtensions = CheckString("LUBELOGGER_ALLOWED_FILE_EXTENSIONS", StaticHelper.DefaultAllowedFileExtensions);
|
||||||
@@ -246,7 +258,8 @@ namespace CarCareTracker.Helper
|
|||||||
ReminderUrgencyConfig = _config.GetSection(nameof(UserConfig.ReminderUrgencyConfig)).Get<ReminderUrgencyConfig>() ?? new ReminderUrgencyConfig(),
|
ReminderUrgencyConfig = _config.GetSection(nameof(UserConfig.ReminderUrgencyConfig)).Get<ReminderUrgencyConfig>() ?? new ReminderUrgencyConfig(),
|
||||||
DefaultTab = (ImportMode)int.Parse(CheckString(nameof(UserConfig.DefaultTab), "8")),
|
DefaultTab = (ImportMode)int.Parse(CheckString(nameof(UserConfig.DefaultTab), "8")),
|
||||||
DefaultReminderEmail = CheckString(nameof(UserConfig.DefaultReminderEmail)),
|
DefaultReminderEmail = CheckString(nameof(UserConfig.DefaultReminderEmail)),
|
||||||
DisableRegistration = CheckBool(CheckString(nameof(UserConfig.DisableRegistration)))
|
DisableRegistration = CheckBool(CheckString(nameof(UserConfig.DisableRegistration))),
|
||||||
|
ShowVehicleThumbnail = CheckBool(CheckString(nameof(UserConfig.ShowVehicleThumbnail)))
|
||||||
};
|
};
|
||||||
int userId = 0;
|
int userId = 0;
|
||||||
if (user != null)
|
if (user != null)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ namespace CarCareTracker.Helper
|
|||||||
{
|
{
|
||||||
private readonly MailConfig mailConfig;
|
private readonly MailConfig mailConfig;
|
||||||
private readonly string serverLanguage;
|
private readonly string serverLanguage;
|
||||||
|
private readonly string serverDomain;
|
||||||
private readonly IFileHelper _fileHelper;
|
private readonly IFileHelper _fileHelper;
|
||||||
private readonly ITranslationHelper _translator;
|
private readonly ITranslationHelper _translator;
|
||||||
private readonly ILogger<MailHelper> _logger;
|
private readonly ILogger<MailHelper> _logger;
|
||||||
@@ -29,6 +30,7 @@ namespace CarCareTracker.Helper
|
|||||||
//load mailConfig from Configuration
|
//load mailConfig from Configuration
|
||||||
mailConfig = config.GetMailConfig();
|
mailConfig = config.GetMailConfig();
|
||||||
serverLanguage = config.GetServerLanguage();
|
serverLanguage = config.GetServerLanguage();
|
||||||
|
serverDomain = config.GetServerDomain();
|
||||||
_fileHelper = fileHelper;
|
_fileHelper = fileHelper;
|
||||||
_translator = translationHelper;
|
_translator = translationHelper;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -43,7 +45,14 @@ namespace CarCareTracker.Helper
|
|||||||
return OperationResponse.Failed("Email Address or Token is invalid");
|
return OperationResponse.Failed("Email Address or Token is invalid");
|
||||||
}
|
}
|
||||||
string emailSubject = _translator.Translate(serverLanguage, "Your Registration Token for LubeLogger");
|
string emailSubject = _translator.Translate(serverLanguage, "Your Registration Token for LubeLogger");
|
||||||
string emailBody = $"{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please complete your registration for LubeLogger using the token")}: {token}";
|
string tokenHtml = token;
|
||||||
|
if (!string.IsNullOrWhiteSpace(serverDomain))
|
||||||
|
{
|
||||||
|
string cleanedURL = serverDomain.EndsWith('/') ? serverDomain.TrimEnd('/') : serverDomain;
|
||||||
|
//construct registration URL.
|
||||||
|
tokenHtml = $"<a href='{cleanedURL}/Login/Registration?email={emailAddress}&token={token}' target='_blank'>{token}</a>";
|
||||||
|
}
|
||||||
|
string emailBody = $"<span>{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please complete your registration for LubeLogger using the token")}: {tokenHtml}</span>";
|
||||||
var result = SendEmail(new List<string> { emailAddress }, emailSubject, emailBody);
|
var result = SendEmail(new List<string> { emailAddress }, emailSubject, emailBody);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
@@ -64,7 +73,14 @@ namespace CarCareTracker.Helper
|
|||||||
return OperationResponse.Failed("Email Address or Token is invalid");
|
return OperationResponse.Failed("Email Address or Token is invalid");
|
||||||
}
|
}
|
||||||
string emailSubject = _translator.Translate(serverLanguage, "Your Password Reset Token for LubeLogger");
|
string emailSubject = _translator.Translate(serverLanguage, "Your Password Reset Token for LubeLogger");
|
||||||
string emailBody = $"{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please reset your password for LubeLogger using the token")}: {token}";
|
string tokenHtml = token;
|
||||||
|
if (!string.IsNullOrWhiteSpace(serverDomain))
|
||||||
|
{
|
||||||
|
string cleanedURL = serverDomain.EndsWith('/') ? serverDomain.TrimEnd('/') : serverDomain;
|
||||||
|
//construct registration URL.
|
||||||
|
tokenHtml = $"<a href='{cleanedURL}/Login/ResetPassword?email={emailAddress}&token={token}' target='_blank'>{token}</a>";
|
||||||
|
}
|
||||||
|
string emailBody = $"<span>{_translator.Translate(serverLanguage, "A token has been generated on your behalf, please reset your password for LubeLogger using the token")}: {tokenHtml}</span>";
|
||||||
var result = SendEmail(new List<string> { emailAddress }, emailSubject, emailBody);
|
var result = SendEmail(new List<string> { emailAddress }, emailSubject, emailBody);
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace CarCareTracker.Helper
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class StaticHelper
|
public static class StaticHelper
|
||||||
{
|
{
|
||||||
public const string VersionNumber = "1.4.6";
|
public const string VersionNumber = "1.4.8";
|
||||||
public const string DbName = "data/cartracker.db";
|
public const string DbName = "data/cartracker.db";
|
||||||
public const string UserConfigPath = "data/config/userConfig.json";
|
public const string UserConfigPath = "data/config/userConfig.json";
|
||||||
public const string LegacyUserConfigPath = "config/userConfig.json";
|
public const string LegacyUserConfigPath = "config/userConfig.json";
|
||||||
@@ -22,6 +22,7 @@ namespace CarCareTracker.Helper
|
|||||||
public const string DefaultAllowedFileExtensions = ".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx";
|
public const string DefaultAllowedFileExtensions = ".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx";
|
||||||
public const string SponsorsPath = "https://hargata.github.io/hargata/sponsors.json";
|
public const string SponsorsPath = "https://hargata.github.io/hargata/sponsors.json";
|
||||||
public const string TranslationPath = "https://hargata.github.io/lubelog_translations";
|
public const string TranslationPath = "https://hargata.github.io/lubelog_translations";
|
||||||
|
public const string ReleasePath = "https://api.github.com/repos/hargata/lubelog/releases/latest";
|
||||||
public const string TranslationDirectoryPath = $"{TranslationPath}/directory.json";
|
public const string TranslationDirectoryPath = $"{TranslationPath}/directory.json";
|
||||||
public const string ReportNote = "Report generated by LubeLogger, a Free and Open Source Vehicle Maintenance Tracker - LubeLogger.com";
|
public const string ReportNote = "Report generated by LubeLogger, a Free and Open Source Vehicle Maintenance Tracker - LubeLogger.com";
|
||||||
public static string GetTitleCaseReminderUrgency(ReminderUrgency input)
|
public static string GetTitleCaseReminderUrgency(ReminderUrgency input)
|
||||||
|
|||||||
@@ -303,11 +303,11 @@ namespace CarCareTracker.Logic
|
|||||||
//set next reminder
|
//set next reminder
|
||||||
if (results.Any(x => (x.Metric == ReminderMetric.Date || x.Metric == ReminderMetric.Both) && x.Date >= DateTime.Now.Date))
|
if (results.Any(x => (x.Metric == ReminderMetric.Date || x.Metric == ReminderMetric.Both) && x.Date >= DateTime.Now.Date))
|
||||||
{
|
{
|
||||||
resultToAdd.NextReminder = results.Where(x => x.Date >= DateTime.Now.Date).OrderBy(x => x.Date).Select(x => new ReminderExportModel { Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString() }).First();
|
resultToAdd.NextReminder = results.Where(x => x.Date >= DateTime.Now.Date).OrderBy(x => x.Date).Select(x => new ReminderExportModel { Id = x.Id.ToString(), Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString(), Tags = string.Join(' ', x.Tags) }).First();
|
||||||
}
|
}
|
||||||
else if (results.Any(x => (x.Metric == ReminderMetric.Odometer || x.Metric == ReminderMetric.Both) && x.Mileage >= currentMileage))
|
else if (results.Any(x => (x.Metric == ReminderMetric.Odometer || x.Metric == ReminderMetric.Both) && x.Mileage >= currentMileage))
|
||||||
{
|
{
|
||||||
resultToAdd.NextReminder = results.Where(x => x.Mileage >= currentMileage).OrderBy(x => x.Mileage).Select(x => new ReminderExportModel { Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString() }).First();
|
resultToAdd.NextReminder = results.Where(x => x.Mileage >= currentMileage).OrderBy(x => x.Mileage).Select(x => new ReminderExportModel { Id = x.Id.ToString(), Description = x.Description, Urgency = x.Urgency.ToString(), Metric = x.Metric.ToString(), Notes = x.Notes, DueDate = x.Date.ToShortDateString(), DueOdometer = x.Mileage.ToString(), Tags = string.Join(' ', x.Tags) }).First();
|
||||||
}
|
}
|
||||||
apiResult.Add(resultToAdd);
|
apiResult.Add(resultToAdd);
|
||||||
}
|
}
|
||||||
|
|||||||
18
Models/API/ReleaseVersion.cs
Normal file
18
Models/API/ReleaseVersion.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// For deserializing GitHub response for latest version
|
||||||
|
/// </summary>
|
||||||
|
public class ReleaseResponse
|
||||||
|
{
|
||||||
|
public string tag_name { get; set; }
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// For returning the version numbers via API.
|
||||||
|
/// </summary>
|
||||||
|
public class ReleaseVersion
|
||||||
|
{
|
||||||
|
public string CurrentVersion { get; set; }
|
||||||
|
public string LatestVersion { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
public bool DisableRegularLogin { get; set; } = false;
|
public bool DisableRegularLogin { get; set; } = false;
|
||||||
public bool UsePKCE { get; set; } = false;
|
public bool UsePKCE { get; set; } = false;
|
||||||
public string LogOutURL { get; set; } = "";
|
public string LogOutURL { get; set; } = "";
|
||||||
|
public string UserInfoURL { get; set; } = "";
|
||||||
public string RemoteAuthURL { get {
|
public string RemoteAuthURL { get {
|
||||||
var redirectUrl = $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}";
|
var redirectUrl = $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}";
|
||||||
if (UsePKCE)
|
if (UsePKCE)
|
||||||
|
|||||||
@@ -3,5 +3,6 @@
|
|||||||
public class OpenIDResult
|
public class OpenIDResult
|
||||||
{
|
{
|
||||||
public string id_token { get; set; }
|
public string id_token { get; set; }
|
||||||
|
public string access_token { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
Models/OIDC/OpenIDUserInfo.cs
Normal file
7
Models/OIDC/OpenIDUserInfo.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class OpenIDUserInfo
|
||||||
|
{
|
||||||
|
public string email { get; set; } = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
10
Models/Report/ReportHeader.cs
Normal file
10
Models/Report/ReportHeader.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class ReportHeader
|
||||||
|
{
|
||||||
|
public int MaxOdometer { get; set; }
|
||||||
|
public int DistanceTraveled { get; set; }
|
||||||
|
public decimal TotalCost { get; set; }
|
||||||
|
public string AverageMPG { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
public class ReportViewModel
|
public class ReportViewModel
|
||||||
{
|
{
|
||||||
|
public ReportHeader ReportHeaderForVehicle { get; set; } = new ReportHeader();
|
||||||
public List<CostForVehicleByMonth> CostForVehicleByMonth { get; set; } = new List<CostForVehicleByMonth>();
|
public List<CostForVehicleByMonth> CostForVehicleByMonth { get; set; } = new List<CostForVehicleByMonth>();
|
||||||
public MPGForVehicleByMonth FuelMileageForVehicleByMonth { get; set; } = new MPGForVehicleByMonth();
|
public MPGForVehicleByMonth FuelMileageForVehicleByMonth { get; set; } = new MPGForVehicleByMonth();
|
||||||
public CostMakeUpForVehicle CostMakeUpForVehicle { get; set; } = new CostMakeUpForVehicle();
|
public CostMakeUpForVehicle CostMakeUpForVehicle { get; set; } = new CostMakeUpForVehicle();
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ namespace CarCareTracker.Models
|
|||||||
}
|
}
|
||||||
public class ReminderExportModel
|
public class ReminderExportModel
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(FromIntOptional))]
|
||||||
|
public string Id { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string Urgency { get; set; }
|
public string Urgency { get; set; }
|
||||||
public string Metric { get; set; }
|
public string Metric { get; set; }
|
||||||
@@ -123,6 +125,7 @@ namespace CarCareTracker.Models
|
|||||||
public string DueDate { get; set; }
|
public string DueDate { get; set; }
|
||||||
[JsonConverter(typeof(FromIntOptional))]
|
[JsonConverter(typeof(FromIntOptional))]
|
||||||
public string DueOdometer { get; set; }
|
public string DueOdometer { get; set; }
|
||||||
|
public string Tags { get; set; }
|
||||||
}
|
}
|
||||||
public class PlanRecordExportModel
|
public class PlanRecordExportModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
public string PreferredGasMileageUnit { get; set; } = string.Empty;
|
public string PreferredGasMileageUnit { get; set; } = string.Empty;
|
||||||
public bool UseUnitForFuelCost { get; set; }
|
public bool UseUnitForFuelCost { get; set; }
|
||||||
public bool ShowCalendar { get; set; }
|
public bool ShowCalendar { get; set; }
|
||||||
|
public bool ShowVehicleThumbnail { get; set; }
|
||||||
public List<UserColumnPreference> UserColumnPreferences { get; set; } = new List<UserColumnPreference>();
|
public List<UserColumnPreference> UserColumnPreferences { get; set; } = new List<UserColumnPreference>();
|
||||||
public ReminderUrgencyConfig ReminderUrgencyConfig { get; set; } = new ReminderUrgencyConfig();
|
public ReminderUrgencyConfig ReminderUrgencyConfig { get; set; } = new ReminderUrgencyConfig();
|
||||||
public string UserNameHash { get; set; }
|
public string UserNameHash { get; set; }
|
||||||
|
|||||||
@@ -40,6 +40,20 @@
|
|||||||
No Params
|
No Params
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row api-method">
|
||||||
|
<div class="col-1">
|
||||||
|
<span class="badge bg-success">GET</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 copyable testable">
|
||||||
|
<code>/api/version</code>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Returns current version of LubeLogger and checks for updates
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
CheckForUpdate(bool) - checks for update(optional)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row api-method">
|
<div class="row api-method">
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<span class="badge bg-success">GET</span>
|
<span class="badge bg-success">GET</span>
|
||||||
@@ -669,6 +683,65 @@
|
|||||||
vehicleId - Id of Vehicle
|
vehicleId - Id of Vehicle
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row api-method">
|
||||||
|
<div class="col-1">
|
||||||
|
<span class="badge bg-primary">POST</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 copyable">
|
||||||
|
<code>/api/vehicle/reminders/add</code>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Adds Reminder Record to the vehicle
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
vehicleId - Id of Vehicle
|
||||||
|
<br />
|
||||||
|
Body(form-data): {<br />
|
||||||
|
description - Description<br />
|
||||||
|
dueDate - Due Date<br />
|
||||||
|
dueOdometer - Due Odometer reading<br />
|
||||||
|
metric - Date/Odometer/Both<br />
|
||||||
|
notes - notes(optional)<br />
|
||||||
|
tags - tags separated by space(optional)<br />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row api-method">
|
||||||
|
<div class="col-1">
|
||||||
|
<span class="badge text-bg-warning">PUT</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 copyable">
|
||||||
|
<code>/api/vehicle/reminders/update</code>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Updates Reminder Record
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Body(form-data): {<br />
|
||||||
|
Id - Id of Reminder Record<br />
|
||||||
|
description - Description<br />
|
||||||
|
dueDate - Due Date<br />
|
||||||
|
dueOdometer - Due Odometer reading<br />
|
||||||
|
metric - Date/Odometer/Both<br />
|
||||||
|
notes - notes(optional)<br />
|
||||||
|
tags - tags separated by space(optional)<br />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row api-method">
|
||||||
|
<div class="col-1">
|
||||||
|
<span class="badge text-bg-danger">DELETE</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 copyable">
|
||||||
|
<code>/api/vehicle/reminders/delete</code>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Deletes Reminder Record
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Id - Id of Reminder Record
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row api-method">
|
<div class="row api-method">
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
<span class="badge bg-success">GET</span>
|
<span class="badge bg-success">GET</span>
|
||||||
|
|||||||
@@ -13,17 +13,21 @@
|
|||||||
emailServerIsSetup = false;
|
emailServerIsSetup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@section Nav {
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="d-flex lubelogger-navbar">
|
||||||
|
<div class="me-2" style="cursor:pointer;" onclick="returnToGarage()">
|
||||||
|
<img src="@config.GetSmallLogoUrl()" class="lubelogger-logo" />
|
||||||
|
</div>
|
||||||
|
<span class="text-truncate lead">@translator.Translate(userLanguage, "Admin Panel")</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@model AdminViewModel
|
@model AdminViewModel
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row d-flex align-items-center justify-content-between justify-content-md-start">
|
|
||||||
<div class="col-2 col-md-1">
|
|
||||||
<a href="/Home" class="btn btn-secondary btn-md mt-1 mb-1"><i class="bi bi-arrow-left-square"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-md-7 text-end text-md-start">
|
|
||||||
<span class="display-6">@translator.Translate(userLanguage, "Admin Panel")</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -56,23 +60,23 @@
|
|||||||
<div class="modal-dialog modal-lg" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="lead">@translator.Translate(userLanguage, "Tokens")</span>
|
<span class="lead">@translator.Translate(userLanguage, "Tokens")</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center ms-auto">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button onclick="generateNewToken()" class="btn btn-primary btn-md mt-1 mb-1">
|
||||||
|
<i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Generate")
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-primary btn-md mt-1 mb-1" @(emailServerIsSetup ? "" : "disabled") onclick="toggleAutoNotify(event)">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="enableAutoNotify" @(emailServerIsSetup ? "checked" : "disabled")>
|
||||||
|
<label class="form-check-label" for="enableAutoNotify">@translator.Translate(userLanguage, "Notify")</label>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center ms-auto">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button onclick="generateNewToken()" class="btn btn-primary btn-md mt-1 mb-1">
|
|
||||||
<i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Generate")
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-outline-primary btn-md mt-1 mb-1" @(emailServerIsSetup ? "" : "disabled") onclick="toggleAutoNotify(event)">
|
|
||||||
<div class="form-check">
|
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="enableAutoNotify" @(emailServerIsSetup ? "checked" : "disabled")>
|
|
||||||
<label class="form-check-label" for="enableAutoNotify">@translator.Translate(userLanguage, "Notify")</label>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button class="btn btn-close btn-md mt-1 mb-1 ms-2" onclick="hideTokenModal()" aria-label="Close"></button>
|
<button class="btn btn-close btn-md mt-1 mb-1 ms-2" onclick="hideTokenModal()" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
|
|||||||
@@ -16,6 +16,71 @@
|
|||||||
<script src="~/js/supplyrecord.js?v=@StaticHelper.VersionNumber"></script>
|
<script src="~/js/supplyrecord.js?v=@StaticHelper.VersionNumber"></script>
|
||||||
<script src="~/lib/drawdown/drawdown.js"></script>
|
<script src="~/lib/drawdown/drawdown.js"></script>
|
||||||
}
|
}
|
||||||
|
@section Nav {
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="d-flex lubelogger-navbar">
|
||||||
|
<div class="me-2" style="cursor:pointer;" onclick="returnToGarage()">
|
||||||
|
<img src="@config.GetSmallLogoUrl()" class="lubelogger-logo lubelogger-tab" />
|
||||||
|
<img src="@config.GetLogoUrl()" class="lubelogger-logo lubelogger-mobile-nav-show" />
|
||||||
|
</div>
|
||||||
|
<ul class="nav nav-tabs lubelogger-tab flex-grow-1" id="homeTab" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link resizable-nav-link @(Model == "garage" ? "active" : "")" oncontextmenu="sortGarage(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><i class="bi bi-car-front"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Garage")</span></button>
|
||||||
|
</li>
|
||||||
|
@if (config.GetServerEnableShopSupplies())
|
||||||
|
{
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link resizable-nav-link" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Supplies")</span></button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@if (userConfig.ShowCalendar)
|
||||||
|
{
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link resizable-nav-link" id="calendar-tab" data-bs-toggle="tab" data-bs-target="#calendar-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-calendar-week"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Calendar")</span></button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
<li class="nav-item ms-auto" role="presentation">
|
||||||
|
<button class="nav-link resizable-nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><i class="bi bi-gear"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Settings")</span></button>
|
||||||
|
</li>
|
||||||
|
@if (User.IsInRole("CookieAuth") || User.IsInRole("APIAuth"))
|
||||||
|
{
|
||||||
|
<li class="nav-item dropdown" role="presentation">
|
||||||
|
<a class="nav-link resizable-nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false"><i class="bi bi-person"></i><span class="ms-2 d-sm-none d-md-inline">@User.Identity.Name</span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
@if (User.IsInRole(nameof(UserData.IsAdmin)))
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="/Admin"><i class="bi bi-people me-2"></i>@translator.Translate(userLanguage, "Admin Panel")</a>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item" onclick="showRootAccountInformationModal()"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item" onclick="showAccountInformationModal()"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</button>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
<li>
|
||||||
|
<button class="dropdown-item" onclick="performLogOut()"><i class="bi bi-box-arrow-right me-2"></i>@translator.Translate(userLanguage, "Logout")</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
<div class="lubelogger-navbar-button">
|
||||||
|
<button type="button" class="btn btn-adaptive" onclick="showMobileNav()"><i class="bi bi-list lubelogger-menu-icon"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
|
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
|
||||||
<ul class="navbar-nav" id="homeTab" role="tablist">
|
<ul class="navbar-nav" id="homeTab" role="tablist">
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
@@ -61,62 +126,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mt-2">
|
|
||||||
<div class="d-flex lubelogger-navbar">
|
|
||||||
<img src="@config.GetLogoUrl()" class="lubelogger-logo" />
|
|
||||||
<div class="lubelogger-navbar-button">
|
|
||||||
<button type="button" class="btn btn-adaptive" onclick="showMobileNav()"><i class="bi bi-list lubelogger-menu-icon"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<ul class="nav nav-tabs lubelogger-tab" id="homeTab" role="tablist">
|
|
||||||
<li class="nav-item" role="presentation">
|
|
||||||
<button class="nav-link resizable-nav-link @(Model == "garage" ? "active" : "")" oncontextmenu="sortGarage(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><i class="bi bi-car-front"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Garage")</span></button>
|
|
||||||
</li>
|
|
||||||
@if (config.GetServerEnableShopSupplies())
|
|
||||||
{
|
|
||||||
<li class="nav-item" role="presentation">
|
|
||||||
<button class="nav-link resizable-nav-link" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Supplies")</span></button>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@if (userConfig.ShowCalendar){
|
|
||||||
<li class="nav-item" role="presentation">
|
|
||||||
<button class="nav-link resizable-nav-link" id="calendar-tab" data-bs-toggle="tab" data-bs-target="#calendar-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-calendar-week"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Calendar")</span></button>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
<li class="nav-item ms-auto" role="presentation">
|
|
||||||
<button class="nav-link resizable-nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><i class="bi bi-gear"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Settings")</span></button>
|
|
||||||
</li>
|
|
||||||
@if (User.IsInRole("CookieAuth") || User.IsInRole("APIAuth"))
|
|
||||||
{
|
|
||||||
<li class="nav-item dropdown" role="presentation">
|
|
||||||
<a class="nav-link resizable-nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false"><i class="bi bi-person"></i><span class="ms-2 d-sm-none d-md-inline">@User.Identity.Name</span></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
@if (User.IsInRole(nameof(UserData.IsAdmin)))
|
|
||||||
{
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="/Admin"><i class="bi bi-people me-2"></i>@translator.Translate(userLanguage,"Admin Panel")</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
|
||||||
{
|
|
||||||
<li>
|
|
||||||
<button class="dropdown-item" onclick="showRootAccountInformationModal()"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</button>
|
|
||||||
</li>
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
<li>
|
|
||||||
<button class="dropdown-item" onclick="showAccountInformationModal()"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</button>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
<li>
|
|
||||||
<button class="dropdown-item" onclick="performLogOut()"><i class="bi bi-box-arrow-right me-2"></i>@translator.Translate(userLanguage,"Logout")</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
<div class="tab-content" id="homeTab">
|
<div class="tab-content" id="homeTab">
|
||||||
<div class="tab-pane fade @(Model == "garage" ? "show active" : "")" id="garage-tab-pane" role="tabpanel" tabindex="0">
|
<div class="tab-pane fade @(Model == "garage" ? "show active" : "")" id="garage-tab-pane" role="tabpanel" tabindex="0">
|
||||||
<div id="garageContainer">
|
<div id="garageContainer">
|
||||||
|
|||||||
@@ -81,9 +81,9 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-4 col-6 garage-item-add">
|
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-4 col-6 garage-item-add user-select-none">
|
||||||
<div class="card" onclick="showAddVehicleModal()" style="height:100%;">
|
<div class="card" onclick="showAddVehicleModal()" style="height:100%;">
|
||||||
<img src="/defaults/addnew_vehicle.png" style="object-fit:scale-down;height:100%;" />
|
<img src="/defaults/addnew_vehicle.png" style="object-fit:scale-down;height:100%;pointer-events:none;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,6 +165,14 @@
|
|||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<input type="text" readonly id="inputOIDCToken" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.TokenURL">
|
<input type="text" readonly id="inputOIDCToken" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.TokenURL">
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mb-2">
|
||||||
|
<div class="col-md-6 col-12">
|
||||||
|
<label for="inputOIDCUserInfo">@translator.Translate(userLanguage, "OIDC UserInfo URL")</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 col-12">
|
||||||
|
<input type="text" readonly id="inputOIDCUserInfo" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.UserInfoURL">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
|
|||||||
@@ -90,6 +90,10 @@
|
|||||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="showCalendar" checked="@Model.UserConfig.ShowCalendar">
|
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="showCalendar" checked="@Model.UserConfig.ShowCalendar">
|
||||||
<label class="form-check-label" for="showCalendar">@translator.Translate(userLanguage, "Show Calendar")</label>
|
<label class="form-check-label" for="showCalendar">@translator.Translate(userLanguage, "Show Calendar")</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-check form-switch form-check-inline">
|
||||||
|
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="showVehicleThumbnail" checked="@Model.UserConfig.ShowVehicleThumbnail">
|
||||||
|
<label class="form-check-label" for="showCalendar">@translator.Translate(userLanguage, "Show Vehicle Thumbnail in Header")</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
var userLanguage = config.GetServerLanguage();
|
var userLanguage = config.GetServerLanguage();
|
||||||
var openRegistrationEnabled = config.GetServerOpenRegistration();
|
var openRegistrationEnabled = config.GetServerOpenRegistration();
|
||||||
}
|
}
|
||||||
|
@model LoginModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Register";
|
ViewData["Title"] = "Register";
|
||||||
}
|
}
|
||||||
@@ -19,18 +20,18 @@
|
|||||||
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
|
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
|
||||||
@if (openRegistrationEnabled) {
|
@if (openRegistrationEnabled) {
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" id="inputToken" class="form-control">
|
<input type="text" id="inputToken" class="form-control" value="@Model.Token">
|
||||||
<div class="input-group-text">
|
<div class="input-group-text">
|
||||||
<button type="button" class="btn btn-sm text-secondary password-visible-button" onclick="sendRegistrationToken()"><i class="bi bi-send"></i></button>
|
<button type="button" class="btn btn-sm text-secondary password-visible-button" onclick="sendRegistrationToken()"><i class="bi bi-send"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
} else {
|
} else {
|
||||||
<input type="text" id="inputToken" class="form-control">
|
<input type="text" id="inputToken" class="form-control" value="@Model.Token">
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputEmail">@translator.Translate(userLanguage, "Email Address")</label>
|
<label for="inputEmail">@translator.Translate(userLanguage, "Email Address")</label>
|
||||||
<input type="text" id="inputEmail" class="form-control">
|
<input type="text" id="inputEmail" class="form-control" value="@Model.EmailAddress">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputUserName">@translator.Translate(userLanguage, "Username")</label>
|
<label for="inputUserName">@translator.Translate(userLanguage, "Username")</label>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
@{
|
@{
|
||||||
var userLanguage = config.GetServerLanguage();
|
var userLanguage = config.GetServerLanguage();
|
||||||
}
|
}
|
||||||
|
@model LoginModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Reset Password";
|
ViewData["Title"] = "Reset Password";
|
||||||
}
|
}
|
||||||
@@ -16,11 +17,11 @@
|
|||||||
<img src="@config.GetLogoUrl()" class="lubelogger-logo" />
|
<img src="@config.GetLogoUrl()" class="lubelogger-logo" />
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
|
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
|
||||||
<input type="text" id="inputToken" class="form-control">
|
<input type="text" id="inputToken" class="form-control" value="@Model.Token">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputUserName">@translator.Translate(userLanguage, "Email Address")</label>
|
<label for="inputUserName">@translator.Translate(userLanguage, "Email Address")</label>
|
||||||
<input type="text" id="inputEmail" class="form-control">
|
<input type="text" id="inputEmail" class="form-control" value="@Model.EmailAddress">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputUserPassword">@translator.Translate(userLanguage, "New Password")</label>
|
<label for="inputUserPassword">@translator.Translate(userLanguage, "New Password")</label>
|
||||||
|
|||||||
@@ -161,6 +161,7 @@
|
|||||||
@await RenderSectionAsync("Scripts", required: false)
|
@await RenderSectionAsync("Scripts", required: false)
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@await RenderSectionAsync("Nav", required: false)
|
||||||
<div class="container" style="height:85vh;">
|
<div class="container" style="height:85vh;">
|
||||||
<main role="main">
|
<main role="main">
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
|
|||||||
@@ -25,6 +25,61 @@
|
|||||||
<script src="~/lib/chart-js/chart.umd.js"></script>
|
<script src="~/lib/chart-js/chart.umd.js"></script>
|
||||||
<script src="~/lib/drawdown/drawdown.js"></script>
|
<script src="~/lib/drawdown/drawdown.js"></script>
|
||||||
}
|
}
|
||||||
|
@section Nav{
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-2">
|
||||||
|
<div class="d-flex lubelogger-navbar">
|
||||||
|
<div class="me-2" style="cursor:pointer;" onclick="returnToGarage()">
|
||||||
|
@if(userConfig.ShowVehicleThumbnail) {
|
||||||
|
<img src="@Model.ImageLocation" class="lubelogger-vehicle-logo @(string.IsNullOrWhiteSpace(Model.SoldDate) ? "" : "sold")" />
|
||||||
|
} else {
|
||||||
|
<img src="@config.GetSmallLogoUrl()" class="lubelogger-logo" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<ul class="nav nav-tabs lubelogger-tab flex-grow-1" id="vehicleTab" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.Dashboard)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.Dashboard)" id="report-tab" data-bs-toggle="tab" data-bs-target="#report-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-file-bar-graph"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Dashboard")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.PlanRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.PlanRecord)" id="plan-tab" data-bs-toggle="tab" data-bs-target="#plan-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-bar-chart-steps"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Planner")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.OdometerRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.OdometerRecord)" id="odometer-tab" data-bs-toggle="tab" data-bs-target="#odometer-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-speedometer"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Odometer")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.ServiceRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab" data-bs-toggle="tab" data-bs-target="#servicerecord-tab-pane" type="button" role="tab" aria-selected="true"><i class="bi bi-card-checklist"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Service Records")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.RepairRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.RepairRecord)" id="accident-tab" data-bs-toggle="tab" data-bs-target="#accident-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-exclamation-octagon"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Repairs")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.UpgradeRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.UpgradeRecord)" id="upgrade-tab" data-bs-toggle="tab" data-bs-target="#upgrade-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-wrench-adjustable"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Upgrades")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.GasRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.GasRecord)" id="gas-tab" data-bs-toggle="tab" data-bs-target="#gas-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-fuel-pump"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Fuel")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.SupplyRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.SupplyRecord)" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Supplies")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.TaxRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.TaxRecord)" id="tax-tab" data-bs-toggle="tab" data-bs-target="#tax-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-currency-dollar"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Taxes")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.NoteRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.NoteRecord)" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-journal-bookmark"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Notes")</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.ReminderRecord)">
|
||||||
|
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ReminderRecord)" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><div class="reminderBellDiv" style="display:inline-flex;"><i class="reminderBell bi bi-bell"></i></div><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Reminders")</span></button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<span style="cursor:pointer;" onclick="editVehicle(@Model.Id)" class="text-truncate"><span class="lead">@($"{Model.Year} {Model.Make} {Model.Model}")<small class="text-body-secondary">@($"(#{StaticHelper.GetVehicleIdentifier(Model)})")</small></span><span class="ms-2 lubelogger-tab"><i class="bi bi-pencil-square"></i></span></span>
|
||||||
|
<div class="lubelogger-navbar-button">
|
||||||
|
<button type="button" class="btn btn-adaptive" onclick="showMobileNav()"><i class="bi bi-list lubelogger-menu-icon"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
|
<div class="lubelogger-mobile-nav" onclick="hideMobileNav()">
|
||||||
<ul class="nav navbar-nav" id="vehicleTab" role="tablist">
|
<ul class="nav navbar-nav" id="vehicleTab" role="tablist">
|
||||||
<li class="nav-item" role="presentation" style="order: -2">
|
<li class="nav-item" role="presentation" style="order: -2">
|
||||||
@@ -69,52 +124,6 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
|
||||||
<div class="d-flex justify-content-between">
|
|
||||||
<button onclick="returnToGarage()" class="lubelogger-tab btn btn-secondary btn-md mt-1 mb-1"><i class="bi bi-arrow-left-square"></i></button>
|
|
||||||
<h1 class="text-truncate display-4">@($"{Model.Year} {Model.Make} {Model.Model}")<small class="text-body-secondary">@($"(#{StaticHelper.GetVehicleIdentifier(Model)})")</small></h1>
|
|
||||||
<button onclick="editVehicle(@Model.Id)" class="lubelogger-tab btn btn-warning btn-md mt-1 mb-1"><i class="bi bi-pencil-square"></i></button>
|
|
||||||
<div class="lubelogger-navbar-button">
|
|
||||||
<button type="button" class="btn btn-adaptive" onclick="showMobileNav()"><i class="bi bi-list lubelogger-menu-icon"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<ul class="nav nav-tabs lubelogger-tab" id="vehicleTab" role="tablist">
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.Dashboard)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.Dashboard)" id="report-tab" data-bs-toggle="tab" data-bs-target="#report-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-file-bar-graph"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Dashboard")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.PlanRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.PlanRecord)" id="plan-tab" data-bs-toggle="tab" data-bs-target="#plan-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-bar-chart-steps"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Planner")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.OdometerRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.OdometerRecord)" id="odometer-tab" data-bs-toggle="tab" data-bs-target="#odometer-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-speedometer"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Odometer")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.ServiceRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab" data-bs-toggle="tab" data-bs-target="#servicerecord-tab-pane" type="button" role="tab" aria-selected="true"><i class="bi bi-card-checklist"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Service Records")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.RepairRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.RepairRecord)" id="accident-tab" data-bs-toggle="tab" data-bs-target="#accident-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-exclamation-octagon"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Repairs")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.UpgradeRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.UpgradeRecord)" id="upgrade-tab" data-bs-toggle="tab" data-bs-target="#upgrade-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-wrench-adjustable"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Upgrades")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.GasRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.GasRecord)" id="gas-tab" data-bs-toggle="tab" data-bs-target="#gas-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-fuel-pump"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Fuel")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.SupplyRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.SupplyRecord)" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Supplies")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.TaxRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.TaxRecord)" id="tax-tab" data-bs-toggle="tab" data-bs-target="#tax-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-currency-dollar"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Taxes")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.NoteRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.NoteRecord)" id="notes-tab" data-bs-toggle="tab" data-bs-target="#notes-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-journal-bookmark"></i><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Notes")</span></button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation" style="order: @userConfig.TabOrder.FindIndex(x=>x == ImportMode.ReminderRecord)">
|
|
||||||
<button class="nav-link resizable-nav-link @StaticHelper.DefaultActiveTab(userConfig, ImportMode.ReminderRecord)" id="reminder-tab" data-bs-toggle="tab" data-bs-target="#reminder-tab-pane" type="button" role="tab" aria-selected="false"><div class="reminderBellDiv" style="display:inline-flex;"><i class="reminderBell bi bi-bell"></i></div><span class="ms-2 d-sm-none d-md-inline">@translator.Translate(userLanguage, "Reminders")</span></button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="tab-content" id="vehicleTabContent">
|
<div class="tab-content" id="vehicleTabContent">
|
||||||
<div class="tab-pane fade @StaticHelper.DefaultActiveTabContent(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab-pane" role="tabpanel" tabindex="0"></div>
|
<div class="tab-pane fade @StaticHelper.DefaultActiveTabContent(userConfig, ImportMode.ServiceRecord)" id="servicerecord-tab-pane" role="tabpanel" tabindex="0"></div>
|
||||||
<div class="tab-pane fade @StaticHelper.DefaultActiveTabContent(userConfig, ImportMode.GasRecord)" id="gas-tab-pane" role="tabpanel" tabindex="0"></div>
|
<div class="tab-pane fade @StaticHelper.DefaultActiveTabContent(userConfig, ImportMode.GasRecord)" id="gas-tab-pane" role="tabpanel" tabindex="0"></div>
|
||||||
|
|||||||
49
Views/Vehicle/_ExtraFieldMultiple.cshtml
Normal file
49
Views/Vehicle/_ExtraFieldMultiple.cshtml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
@using CarCareTracker.Helper
|
||||||
|
@inject IConfigHelper config
|
||||||
|
@inject ITranslationHelper translator
|
||||||
|
@{
|
||||||
|
var userConfig = config.GetUserConfig(User);
|
||||||
|
var userLanguage = userConfig.UserLanguage;
|
||||||
|
}
|
||||||
|
@model List<ExtraField>
|
||||||
|
@if (Model.Any()){
|
||||||
|
@foreach (ExtraField field in Model)
|
||||||
|
{
|
||||||
|
var elementId = Guid.NewGuid();
|
||||||
|
<div class="extra-field">
|
||||||
|
<label for="@elementId">@field.Name</label>
|
||||||
|
@switch(field.FieldType){
|
||||||
|
case (ExtraFieldType.Text):
|
||||||
|
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
break;
|
||||||
|
case (ExtraFieldType.Number):
|
||||||
|
<input type="number" inputmode="numeric" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
break;
|
||||||
|
case (ExtraFieldType.Decimal):
|
||||||
|
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="fixDecimalInput(this, 2)" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
break;
|
||||||
|
case (ExtraFieldType.Date):
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
|
||||||
|
</div>
|
||||||
|
<script>initExtraFieldDatePicker('@elementId')</script>
|
||||||
|
break;
|
||||||
|
case (ExtraFieldType.Time):
|
||||||
|
<input type="time" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
break;
|
||||||
|
case (ExtraFieldType.Location):
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<button type="button" class="btn btn-sm btn-primary zero-y-padding" onclick="populateLocationField('@elementId')"><i class="bi bi-geo-alt"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
{
|
{
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<a type="button" class="btn btn-link text-truncate uploadedFileName" href="@filesUploaded.Location" target="_blank">@filesUploaded.Name</a>
|
<a type="button" class="btn btn-link text-truncate uploadedFileName" href="@filesUploaded.Location" title="@filesUploaded.Name" target="_blank">@filesUploaded.Name</a>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary me-2" onclick="editFileName('@filesUploaded.Location', this)"><i class="bi bi-pencil"></i></button>
|
<button type="button" class="btn btn-sm btn-outline-secondary me-2" onclick="editFileName('@filesUploaded.Location', this)"><i class="bi bi-pencil"></i></button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteFileFromUploadedFiles('@filesUploaded.Location', this)"><i class="bi bi-trash"></i></button>
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteFileFromUploadedFiles('@filesUploaded.Location', this)"><i class="bi bi-trash"></i></button>
|
||||||
|
|||||||
@@ -97,14 +97,7 @@
|
|||||||
<!option value="@tag">@tag</!option>
|
<!option value="@tag">@tag</!option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
@foreach (ExtraField field in Model.GasRecord.ExtraFields)
|
@await Html.PartialAsync("_ExtraField", Model.GasRecord.ExtraFields)
|
||||||
{
|
|
||||||
var elementId = Guid.NewGuid();
|
|
||||||
<div class="extra-field">
|
|
||||||
<label for="@elementId">@field.Name</label>
|
|
||||||
<input type="text" id="@elementId" class="form-control @(field.IsRequired ? "extra-field-required" : "")" placeholder="@field.Name" value="@field.Value">
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="gasRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
<label for="gasRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||||
|
|||||||
@@ -30,14 +30,7 @@
|
|||||||
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="@(useThreeDecimals ? "fixDecimalInput(this, 3)" : "fixDecimalInput(this, 2)")" id="gasRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="@(useThreeDecimals ? "fixDecimalInput(this, 3)" : "fixDecimalInput(this, 2)")" id="gasRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
<label for="gasRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
|
<label for="gasRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
|
||||||
<select multiple class="form-select" id="gasRecordTag"></select>
|
<select multiple class="form-select" id="gasRecordTag"></select>
|
||||||
@foreach (ExtraField field in Model.EditRecord.ExtraFields)
|
@await Html.PartialAsync("_ExtraFieldMultiple", Model.EditRecord.ExtraFields)
|
||||||
{
|
|
||||||
var elementId = Guid.NewGuid();
|
|
||||||
<div class="extra-field">
|
|
||||||
<label for="@elementId">@field.Name</label>
|
|
||||||
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="gasRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
<label for="gasRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||||
|
|||||||
@@ -28,14 +28,7 @@
|
|||||||
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="fixDecimalInput(this, 2)" id="genericRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
<input type="text" inputmode="decimal" onkeydown="interceptDecimalKeys(event)" onkeyup="fixDecimalInput(this, 2)" id="genericRecordCost" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
<label for="genericRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
|
<label for="genericRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
|
||||||
<select multiple class="form-select" id="genericRecordTag"></select>
|
<select multiple class="form-select" id="genericRecordTag"></select>
|
||||||
@foreach (ExtraField field in Model.EditRecord.ExtraFields)
|
@await Html.PartialAsync("_ExtraFieldMultiple", Model.EditRecord.ExtraFields)
|
||||||
{
|
|
||||||
var elementId = Guid.NewGuid();
|
|
||||||
<div class="extra-field">
|
|
||||||
<label for="@elementId">@field.Name</label>
|
|
||||||
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="genericRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
<label for="genericRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||||
|
|||||||
@@ -6,10 +6,15 @@
|
|||||||
var barGraphColors = StaticHelper.GetBarChartColors();
|
var barGraphColors = StaticHelper.GetBarChartColors();
|
||||||
var userConfig = config.GetUserConfig(User);
|
var userConfig = config.GetUserConfig(User);
|
||||||
var userLanguage = userConfig.UserLanguage;
|
var userLanguage = userConfig.UserLanguage;
|
||||||
|
|
||||||
|
var graphGrace = Decimal.ToInt32(Model.CostData.Max(x => x.Cost) - Model.CostData.Min(x => x.Cost));
|
||||||
|
if (graphGrace < 0 || Model.CostData.Min(x=>x.Cost) - graphGrace < 0)
|
||||||
|
{
|
||||||
|
graphGrace = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@if (Model.CostData.Any(x => x.Cost > 0))
|
@if (Model.CostData.Any(x => x.Cost > 0))
|
||||||
{
|
{
|
||||||
|
|
||||||
<canvas id="bar-chart-mpg"></canvas>
|
<canvas id="bar-chart-mpg"></canvas>
|
||||||
<script>
|
<script>
|
||||||
renderChart();
|
renderChart();
|
||||||
@@ -54,7 +59,8 @@
|
|||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
beginAtZero: true,
|
beginAtZero: false,
|
||||||
|
grace: decodeHTMLEntities('@(graphGrace)'),
|
||||||
ticks: {
|
ticks: {
|
||||||
color: useDarkMode ? "#fff" : "#000"
|
color: useDarkMode ? "#fff" : "#000"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,14 +33,14 @@
|
|||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
@await Html.PartialAsync("_UploadedFiles", Model.Files)
|
@await Html.PartialAsync("_UploadedFiles", Model.Files)
|
||||||
<label for="serviceRecordFiles">@translator.Translate(userLanguage, "Upload more documents")</label>
|
<label for="noteFiles">@translator.Translate(userLanguage, "Upload more documents")</label>
|
||||||
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="noteFiles">
|
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="noteFiles">
|
||||||
<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
|
<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<label for="serviceRecordFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label>
|
<label for="noteFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label>
|
||||||
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="noteFiles">
|
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="noteFiles">
|
||||||
<br />
|
<br />
|
||||||
<small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
|
<small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
|
||||||
@@ -87,4 +87,4 @@
|
|||||||
function getNoteModelData(){
|
function getNoteModelData(){
|
||||||
return { id: @Model.Id}
|
return { id: @Model.Id}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -26,14 +26,7 @@
|
|||||||
<input type="number" inputmode="numeric" id="odometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
<input type="number" inputmode="numeric" id="odometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
||||||
<label for="odometerRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
|
<label for="odometerRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
|
||||||
<select multiple class="form-select" id="odometerRecordTag"></select>
|
<select multiple class="form-select" id="odometerRecordTag"></select>
|
||||||
@foreach (ExtraField field in Model.EditRecord.ExtraFields)
|
@await Html.PartialAsync("_ExtraFieldMultiple", Model.EditRecord.ExtraFields)
|
||||||
{
|
|
||||||
var elementId = Guid.NewGuid();
|
|
||||||
<div class="extra-field">
|
|
||||||
<label for="@elementId">@field.Name</label>
|
|
||||||
<input type="text" id="@elementId" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="odometerRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
<label for="odometerRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row swimlane">
|
<div class="row swimlane">
|
||||||
<div class="col-3 d-flex flex-column swimlane mid" ondragover="dragOver(event)" ondrop="dropBox(event, 'Backlog')">
|
<div class="col-3 d-flex flex-column swimlane" ondragover="dragOver(event)" ondrop="dropBox(event, 'Backlog')">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
|
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
|
||||||
<span class="display-7">@translator.Translate(userLanguage,"Planned")</span>
|
<span class="display-7">@translator.Translate(userLanguage,"Planned")</span>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
@await Html.PartialAsync("_PlanRecordItem", planRecord)
|
@await Html.PartialAsync("_PlanRecordItem", planRecord)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3 d-flex flex-column swimlane mid" ondragover="dragOver(event)" ondrop="dropBox(event, 'InProgress')">
|
<div class="col-3 d-flex flex-column swimlane" ondragover="dragOver(event)" ondrop="dropBox(event, 'InProgress')">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
|
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
|
||||||
<span class="display-7">@translator.Translate(userLanguage,"Doing")</span>
|
<span class="display-7">@translator.Translate(userLanguage,"Doing")</span>
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
@await Html.PartialAsync("_PlanRecordItem", planRecord)
|
@await Html.PartialAsync("_PlanRecordItem", planRecord)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-3 d-flex flex-column swimlane end" ondragover="dragOver(event)" ondrop="dropBox(event, 'Done')">
|
<div class="col-3 d-flex flex-column swimlane" ondragover="dragOver(event)" ondrop="dropBox(event, 'Done')">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
|
<div class="col-12 d-flex justify-content-center" style="height:5vh;">
|
||||||
<span class="display-7">@translator.Translate(userLanguage,"Done")</span>
|
<span class="display-7">@translator.Translate(userLanguage,"Done")</span>
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
}
|
}
|
||||||
@model ReportViewModel
|
@model ReportViewModel
|
||||||
<div class="container reportTabContainer">
|
<div class="container reportTabContainer">
|
||||||
|
<div class="row hideOnPrint" id="reportHeaderContent">
|
||||||
|
@await Html.PartialAsync("_ReportHeader", Model.ReportHeaderForVehicle)
|
||||||
|
</div>
|
||||||
|
<hr />
|
||||||
<div class="row hideOnPrint">
|
<div class="row hideOnPrint">
|
||||||
<div class="col-md-3 col-12 mt-2">
|
<div class="col-md-3 col-12 mt-2">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
24
Views/Vehicle/_ReportHeader.cshtml
Normal file
24
Views/Vehicle/_ReportHeader.cshtml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
@using CarCareTracker.Helper
|
||||||
|
@inject IConfigHelper config
|
||||||
|
@inject ITranslationHelper translator
|
||||||
|
@{
|
||||||
|
var userConfig = config.GetUserConfig(User);
|
||||||
|
var userLanguage = userConfig.UserLanguage;
|
||||||
|
}
|
||||||
|
@model ReportHeader
|
||||||
|
<div class="col-md-3 col-12 mt-2 text-center">
|
||||||
|
<span class="lead">@Model.MaxOdometer.ToString("N0")</span><br />
|
||||||
|
<span class="text-secondary">@translator.Translate(userLanguage, "Last Reported Odometer Reading")</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-12 mt-2 text-center">
|
||||||
|
<span class="lead">@Model.DistanceTraveled.ToString("N0")</span><br />
|
||||||
|
<span class="text-secondary">@translator.Translate(userLanguage, "Distance Traveled")</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-12 mt-2 text-center">
|
||||||
|
<span class="lead">@StaticHelper.HideZeroCost(Model.TotalCost.ToString("C2"), true)</span><br />
|
||||||
|
<span class="text-secondary">@translator.Translate(userLanguage, "Total Cost")</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-12 mt-2 text-center">
|
||||||
|
<span class="lead">@Model.AverageMPG</span><br />
|
||||||
|
<span class="text-secondary">@translator.Translate(userLanguage, "Average Fuel Economy")</span>
|
||||||
|
</div>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
{
|
{
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<a type="button" class="btn btn-link text-truncate uploadedFileName" href="@filesUploaded.Location" target="_blank">@filesUploaded.Name</a>
|
<a type="button" class="btn btn-link text-truncate uploadedFileName" href="@filesUploaded.Location" title="@filesUploaded.Name" target="_blank">@filesUploaded.Name</a>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<button type="button" class="btn btn-sm btn-outline-secondary me-2" onclick="editFileName('@filesUploaded.Location', this)"><i class="bi bi-pencil"></i></button>
|
<button type="button" class="btn btn-sm btn-outline-secondary me-2" onclick="editFileName('@filesUploaded.Location', this)"><i class="bi bi-pencil"></i></button>
|
||||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteFileFromUploadedFiles('@filesUploaded.Location', this)"><i class="bi bi-trash"></i></button>
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteFileFromUploadedFiles('@filesUploaded.Location', this)"><i class="bi bi-trash"></i></button>
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
"EnableAutoReminderRefresh": false,
|
"EnableAutoReminderRefresh": false,
|
||||||
"EnableAutoOdometerInsert": false,
|
"EnableAutoOdometerInsert": false,
|
||||||
"EnableShopSupplies": false,
|
"EnableShopSupplies": false,
|
||||||
"ShowCalendar": true,
|
"ShowCalendar": true,
|
||||||
|
"ShowVehicleThumbnail": true,
|
||||||
"EnableExtraFieldColumns": false,
|
"EnableExtraFieldColumns": false,
|
||||||
"UseUKMPG": false,
|
"UseUKMPG": false,
|
||||||
"UseThreeDecimalGasCost": true,
|
"UseThreeDecimalGasCost": true,
|
||||||
|
|||||||
@@ -112,6 +112,11 @@
|
|||||||
<input type="text" id="inputSmtpFrom" class="form-control">
|
<input type="text" id="inputSmtpFrom" class="form-control">
|
||||||
<small class="text-body-secondary">Sender Email Address</small>
|
<small class="text-body-secondary">Sender Email Address</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputServerDomain">LubeLogger Domain</label>
|
||||||
|
<input type="text" id="inputServerDomain" class="form-control">
|
||||||
|
<small class="text-body-secondary">For Links in Emails</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,6 +155,11 @@
|
|||||||
<input type="text" id="inputOIDCTokenURL" class="form-control">
|
<input type="text" id="inputOIDCTokenURL" class="form-control">
|
||||||
<small class="text-body-secondary">Token URL from Provider</small>
|
<small class="text-body-secondary">Token URL from Provider</small>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="inputOIDCUserInfoURL">User Info URL</label>
|
||||||
|
<input type="text" id="inputOIDCUserInfoURL" class="form-control">
|
||||||
|
<small class="text-body-secondary">Required by some Providers</small>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="inputOIDCRedirectURL">LubeLogger URL</label>
|
<label for="inputOIDCRedirectURL">LubeLogger URL</label>
|
||||||
<input type="text" id="inputOIDCRedirectURL" class="form-control">
|
<input type="text" id="inputOIDCRedirectURL" class="form-control">
|
||||||
@@ -316,6 +326,9 @@ function generateConfig(){
|
|||||||
if ($("#inputInvariantAPI").is(":checked")){
|
if ($("#inputInvariantAPI").is(":checked")){
|
||||||
windowConfig["LUBELOGGER_INVARIANT_API"]=$('#inputInvariantAPI').is(':checked');
|
windowConfig["LUBELOGGER_INVARIANT_API"]=$('#inputInvariantAPI').is(':checked');
|
||||||
}
|
}
|
||||||
|
if ($("#inputServerDomain").val().trim() != ''){
|
||||||
|
windowConfig["LUBELOGGER_DOMAIN"] = $('#inputServerDomain').val();
|
||||||
|
}
|
||||||
if ($('#inputSmtpServer').val().trim() != ''){
|
if ($('#inputSmtpServer').val().trim() != ''){
|
||||||
windowConfig["MailConfig"] = {
|
windowConfig["MailConfig"] = {
|
||||||
EmailServer: $("#inputSmtpServer").val(),
|
EmailServer: $("#inputSmtpServer").val(),
|
||||||
@@ -332,6 +345,7 @@ function generateConfig(){
|
|||||||
ClientSecret: $("#inputOIDCClientSecret").val(),
|
ClientSecret: $("#inputOIDCClientSecret").val(),
|
||||||
AuthURL: $("#inputOIDCAuthURL").val(),
|
AuthURL: $("#inputOIDCAuthURL").val(),
|
||||||
TokenURL: $("#inputOIDCTokenURL").val(),
|
TokenURL: $("#inputOIDCTokenURL").val(),
|
||||||
|
UserInfoURL: $("#inputOIDCUserInfoURL").val(),
|
||||||
RedirectURL: redirectUrl,
|
RedirectURL: redirectUrl,
|
||||||
Scope: $("#inputOIDCScope").val(),
|
Scope: $("#inputOIDCScope").val(),
|
||||||
ValidateState: $("#inputOIDCValidateState").is(":checked"),
|
ValidateState: $("#inputOIDCValidateState").is(":checked"),
|
||||||
@@ -386,6 +400,9 @@ function generateConfig(){
|
|||||||
if ($("#inputPostgres").val().trim() != ''){
|
if ($("#inputPostgres").val().trim() != ''){
|
||||||
dockerConfig.push(`POSTGRES_CONNECTION="${$('#inputPostgres').val()}"`);
|
dockerConfig.push(`POSTGRES_CONNECTION="${$('#inputPostgres').val()}"`);
|
||||||
}
|
}
|
||||||
|
if ($("#inputServerDomain").val().trim() != ''){
|
||||||
|
dockerConfig.push(`LUBELOGGER_DOMAIN="${$('#inputServerDomain').val()}"`);
|
||||||
|
}
|
||||||
if ($("#inputCustomWidgets").is(":checked")){
|
if ($("#inputCustomWidgets").is(":checked")){
|
||||||
dockerConfig.push(`LUBELOGGER_CUSTOM_WIDGETS="${$('#inputCustomWidgets').is(':checked')}"`);
|
dockerConfig.push(`LUBELOGGER_CUSTOM_WIDGETS="${$('#inputCustomWidgets').is(':checked')}"`);
|
||||||
}
|
}
|
||||||
@@ -405,6 +422,7 @@ function generateConfig(){
|
|||||||
dockerConfig.push(`OpenIDConfig__ClientSecret="${$('#inputOIDCClientSecret').val()}"`);
|
dockerConfig.push(`OpenIDConfig__ClientSecret="${$('#inputOIDCClientSecret').val()}"`);
|
||||||
dockerConfig.push(`OpenIDConfig__AuthURL="${$('#inputOIDCAuthURL').val()}"`);
|
dockerConfig.push(`OpenIDConfig__AuthURL="${$('#inputOIDCAuthURL').val()}"`);
|
||||||
dockerConfig.push(`OpenIDConfig__TokenURL="${$('#inputOIDCTokenURL').val()}"`);
|
dockerConfig.push(`OpenIDConfig__TokenURL="${$('#inputOIDCTokenURL').val()}"`);
|
||||||
|
dockerConfig.push(`OpenIDConfig__UserInfoURL="${$('#inputOIDCUserInfoURL').val()}"`);
|
||||||
dockerConfig.push(`OpenIDConfig__RedirectURL="${redirectUrl}"`);
|
dockerConfig.push(`OpenIDConfig__RedirectURL="${redirectUrl}"`);
|
||||||
dockerConfig.push(`OpenIDConfig__Scope="${$('#inputOIDCScope').val()}"`);
|
dockerConfig.push(`OpenIDConfig__Scope="${$('#inputOIDCScope').val()}"`);
|
||||||
dockerConfig.push(`OpenIDConfig__ValidateState=${$('#inputOIDCValidateState').is(':checked')}`);
|
dockerConfig.push(`OpenIDConfig__ValidateState=${$('#inputOIDCValidateState').is(':checked')}`);
|
||||||
|
|||||||
@@ -48,12 +48,10 @@ html {
|
|||||||
.swimlane{
|
.swimlane{
|
||||||
height:100%;
|
height:100%;
|
||||||
}
|
}
|
||||||
.swimlane.mid {
|
|
||||||
|
.swimlane:not(:last-child) {
|
||||||
border-right-style: solid;
|
border-right-style: solid;
|
||||||
}
|
}
|
||||||
.swimlane.end {
|
|
||||||
border-left-style: solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.showOnPrint {
|
.showOnPrint {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -244,6 +242,18 @@ html {
|
|||||||
background-color: #0d6efd;
|
background-color: #0d6efd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lubelogger-navbar {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lubelogger-tab {
|
||||||
|
border:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lubelogger-tab .nav-link {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
/*Media Queries*/
|
/*Media Queries*/
|
||||||
@media (max-width: 576px) {
|
@media (max-width: 576px) {
|
||||||
.lubelogger-tab {
|
.lubelogger-tab {
|
||||||
@@ -300,10 +310,6 @@ html {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lubelogger-navbar {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lubelogger-navbar-button {
|
.lubelogger-navbar-button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -502,7 +508,8 @@ html[data-bs-theme="light"] .api-method:hover {
|
|||||||
|
|
||||||
.lubelogger-logo {
|
.lubelogger-logo {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
width: 204px;
|
min-width: 48px;
|
||||||
|
max-width: 204px;
|
||||||
object-fit: scale-down;
|
object-fit: scale-down;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@@ -528,4 +535,16 @@ html[data-bs-theme="light"] .api-method:hover {
|
|||||||
font-size: 0.6em;
|
font-size: 0.6em;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
top: 15%
|
top: 15%
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lubelogger-vehicle-logo {
|
||||||
|
height: 48px;
|
||||||
|
width: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lubelogger-vehicle-logo.sold {
|
||||||
|
filter: grayscale(100%);
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
BIN
wwwroot/defaults/lubelogger_logo_small.png
Normal file
BIN
wwwroot/defaults/lubelogger_logo_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
@@ -143,6 +143,14 @@ function refreshMPGChart() {
|
|||||||
var year = getYear();
|
var year = getYear();
|
||||||
$.post('/Vehicle/GetMonthMPGByVehicle', {vehicleId: vehicleId, year: year}, function (data) {
|
$.post('/Vehicle/GetMonthMPGByVehicle', {vehicleId: vehicleId, year: year}, function (data) {
|
||||||
$("#monthFuelMileageReportContent").html(data);
|
$("#monthFuelMileageReportContent").html(data);
|
||||||
|
refreshReportHeader();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function refreshReportHeader() {
|
||||||
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
|
var year = getYear();
|
||||||
|
$.post('/Vehicle/GetSummaryForVehicle', { vehicleId: vehicleId, year: year }, function (data) {
|
||||||
|
$("#reportHeaderContent").html(data);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function setSelectedMetrics() {
|
function setSelectedMetrics() {
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ function updateSettings() {
|
|||||||
enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"),
|
enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"),
|
||||||
enableShopSupplies: $("#enableShopSupplies").is(":checked"),
|
enableShopSupplies: $("#enableShopSupplies").is(":checked"),
|
||||||
showCalendar: $("#showCalendar").is(":checked"),
|
showCalendar: $("#showCalendar").is(":checked"),
|
||||||
|
showVehicleThumbnail: $("#showVehicleThumbnail").is(":checked"),
|
||||||
enableExtraFieldColumns: $("#enableExtraFieldColumns").is(":checked"),
|
enableExtraFieldColumns: $("#enableExtraFieldColumns").is(":checked"),
|
||||||
hideSoldVehicles: $("#hideSoldVehicles").is(":checked"),
|
hideSoldVehicles: $("#hideSoldVehicles").is(":checked"),
|
||||||
preferredGasUnit: $("#preferredGasUnit").val(),
|
preferredGasUnit: $("#preferredGasUnit").val(),
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
function successToast(message) {
|
function returnToGarage() {
|
||||||
|
window.location.href = '/Home';
|
||||||
|
}
|
||||||
|
function successToast(message) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
toast: true,
|
toast: true,
|
||||||
position: "top-end",
|
position: "top-end",
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
function returnToGarage() {
|
$(document).ready(function () {
|
||||||
window.location.href = '/Home';
|
|
||||||
}
|
|
||||||
$(document).ready(function () {
|
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
//bind tabs
|
//bind tabs
|
||||||
$('button[data-bs-toggle="tab"]').on('show.bs.tab', function (e) {
|
$('button[data-bs-toggle="tab"]').on('show.bs.tab', function (e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user