Compare commits

...

21 Commits

Author SHA1 Message Date
Hargata Softworks
27126638be Merge pull request #244 from hargata/Hargata/deltamileage
swap min max label on filter.
2024-02-07 13:12:38 -07:00
DESKTOP-GENO133\IvanPlex
8c3a001930 swap min max label on filter. 2024-02-07 13:11:18 -07:00
Hargata Softworks
2b2f181888 Merge pull request #243 from hargata/Hargata/deltamileage
fixed max and min labels not updating.
2024-02-07 12:49:04 -07:00
DESKTOP-GENO133\IvanPlex
66557fa126 fixed max and min labels not updating. 2024-02-07 12:48:34 -07:00
Hargata Softworks
7c00951d74 Merge pull request #242 from hargata/Hargata/deltamileage
Hargata/deltamileage
2024-02-07 12:32:36 -07:00
DESKTOP-GENO133\IvanPlex
1087ed56ce added feature to auto add to odometer when user adds via API. 2024-02-07 10:51:04 -07:00
DESKTOP-GENO133\IvanPlex
dd2cfd90b1 added method to toggle delta mileage. 2024-02-07 10:41:58 -07:00
Hargata Softworks
cc43d45c9c Merge pull request #234 from hargata/Hargata/email.reminder.template
created email template for reminder emails.
2024-02-06 16:13:04 -07:00
Hargata Softworks
dba51f171d Merge pull request #237 from hargata/Hargata/plan.template
Planner Templates
2024-02-06 16:07:26 -07:00
DESKTOP-GENO133\IvanPlex
0bbf9f783f added more translation keys. 2024-02-06 16:04:34 -07:00
DESKTOP-GENO133\IvanPlex
a52ac6a41b Front end for creating plan records from templates. 2024-02-06 15:54:32 -07:00
DESKTOP-GENO133\IvanPlex
a542c91dc3 added backend for saving and retrieving plan record templates. 2024-02-06 12:45:39 -07:00
DESKTOP-GENO133\IvanPlex
6bcbb0cc86 created email template for reminder emails. 2024-02-06 06:12:13 -07:00
DESKTOP-GENO133\IvanPlex
7a78d51c79 Update version number. 2024-02-05 20:30:13 -07:00
Hargata Softworks
3c2715840e Merge pull request #233 from hargata/Hargata/increase.upload.size
1.1.3
2024-02-05 18:29:36 -07:00
DESKTOP-GENO133\IvanPlex
dfbd90aaf4 Merge branch 'main' into Hargata/increase.upload.size
# Conflicts:
#	Views/Login/Index.cshtml
2024-02-05 18:26:14 -07:00
DESKTOP-GENO133\IvanPlex
2b3a4ce8ce more things to translate 2024-02-05 18:23:06 -07:00
DESKTOP-GENO133\IvanPlex
135292f2b4 added attachment features to Notes. 2024-02-05 18:08:54 -07:00
Hargata Softworks
499c697b0b Merge pull request #229 from hargata/Hargata/redirectURL
added redirectURL
2024-02-05 08:46:05 -07:00
DESKTOP-T0O5CDB\DESK-555BD
fb74f01609 added redirectURL 2024-02-05 08:44:49 -07:00
DESKTOP-GENO133\IvanPlex
6ab2216742 greatly increase the max upload size. 2024-02-04 15:32:34 -07:00
30 changed files with 621 additions and 85 deletions

View File

@@ -29,6 +29,7 @@ namespace CarCareTracker.Controllers
private readonly IUserLogic _userLogic;
private readonly IFileHelper _fileHelper;
private readonly IMailHelper _mailHelper;
private readonly IConfigHelper _config;
public APIController(IVehicleDataAccess dataAccess,
IGasHelper gasHelper,
IReminderHelper reminderHelper,
@@ -44,6 +45,7 @@ namespace CarCareTracker.Controllers
IUserRecordDataAccess userRecordDataAccess,
IMailHelper mailHelper,
IFileHelper fileHelper,
IConfigHelper config,
IUserLogic userLogic)
{
_dataAccess = dataAccess;
@@ -62,6 +64,7 @@ namespace CarCareTracker.Controllers
_reminderHelper = reminderHelper;
_userLogic = userLogic;
_fileHelper = fileHelper;
_config = config;
}
public IActionResult Index()
{
@@ -126,6 +129,17 @@ namespace CarCareTracker.Controllers
Cost = decimal.Parse(input.Cost)
};
_serviceRecordDataAccess.SaveServiceRecordToVehicle(serviceRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
var odometerRecord = new OdometerRecord()
{
VehicleId = vehicleId,
Date = DateTime.Parse(input.Date),
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
}
response.Success = true;
response.Message = "Service Record Added";
return Json(response);
@@ -182,6 +196,17 @@ namespace CarCareTracker.Controllers
Cost = decimal.Parse(input.Cost)
};
_collisionRecordDataAccess.SaveCollisionRecordToVehicle(repairRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
var odometerRecord = new OdometerRecord()
{
VehicleId = vehicleId,
Date = DateTime.Parse(input.Date),
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
}
response.Success = true;
response.Message = "Repair Record Added";
return Json(response);
@@ -238,6 +263,17 @@ namespace CarCareTracker.Controllers
Cost = decimal.Parse(input.Cost)
};
_upgradeRecordDataAccess.SaveUpgradeRecordToVehicle(upgradeRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
var odometerRecord = new OdometerRecord()
{
VehicleId = vehicleId,
Date = DateTime.Parse(input.Date),
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
}
response.Success = true;
response.Message = "Upgrade Record Added";
return Json(response);
@@ -413,6 +449,17 @@ namespace CarCareTracker.Controllers
Cost = decimal.Parse(input.Cost)
};
_gasRecordDataAccess.SaveGasRecordToVehicle(gasRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
var odometerRecord = new OdometerRecord()
{
VehicleId = vehicleId,
Date = DateTime.Parse(input.Date),
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
}
response.Success = true;
response.Message = "Gas Record Added";
return Json(response);

View File

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

View File

@@ -27,6 +27,7 @@ namespace CarCareTracker.Controllers
private readonly IUpgradeRecordDataAccess _upgradeRecordDataAccess;
private readonly ISupplyRecordDataAccess _supplyRecordDataAccess;
private readonly IPlanRecordDataAccess _planRecordDataAccess;
private readonly IPlanRecordTemplateDataAccess _planRecordTemplateDataAccess;
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
private readonly IWebHostEnvironment _webEnv;
private readonly IConfigHelper _config;
@@ -51,6 +52,7 @@ namespace CarCareTracker.Controllers
IUpgradeRecordDataAccess upgradeRecordDataAccess,
ISupplyRecordDataAccess supplyRecordDataAccess,
IPlanRecordDataAccess planRecordDataAccess,
IPlanRecordTemplateDataAccess planRecordTemplateDataAccess,
IOdometerRecordDataAccess odometerRecordDataAccess,
IUserLogic userLogic,
IWebHostEnvironment webEnv,
@@ -71,6 +73,7 @@ namespace CarCareTracker.Controllers
_upgradeRecordDataAccess = upgradeRecordDataAccess;
_supplyRecordDataAccess = supplyRecordDataAccess;
_planRecordDataAccess = planRecordDataAccess;
_planRecordTemplateDataAccess = planRecordTemplateDataAccess;
_odometerRecordDataAccess = odometerRecordDataAccess;
_userLogic = userLogic;
_webEnv = webEnv;
@@ -142,6 +145,7 @@ namespace CarCareTracker.Controllers
_reminderRecordDataAccess.DeleteAllReminderRecordsByVehicleId(vehicleId) &&
_upgradeRecordDataAccess.DeleteAllUpgradeRecordsByVehicleId(vehicleId) &&
_planRecordDataAccess.DeleteAllPlanRecordsByVehicleId(vehicleId) &&
_planRecordTemplateDataAccess.DeleteAllPlanRecordTemplatesByVehicleId(vehicleId) &&
_supplyRecordDataAccess.DeleteAllSupplyRecordsByVehicleId(vehicleId) &&
_userLogic.DeleteAllAccessToVehicle(vehicleId) &&
_dataAccess.DeleteVehicle(vehicleId);
@@ -1467,6 +1471,21 @@ namespace CarCareTracker.Controllers
}
#endregion
#region "Supply Records"
private List<string> CheckSupplyRecordsAvailability(List<SupplyUsage> supplyUsage)
{
//returns empty string if all supplies are available
var result = new List<string>();
foreach (SupplyUsage supply in supplyUsage)
{
//get supply record.
var supplyData = _supplyRecordDataAccess.GetSupplyRecordById(supply.SupplyId);
if (supply.Quantity > supplyData.Quantity)
{
result.Add($"Insufficient Quantity for {supplyData.Description}, need: {supply.Quantity}, available: {supplyData.Quantity}");
}
}
return result;
}
private void RequisitionSupplyRecordsByUsage(List<SupplyUsage> supplyUsage)
{
foreach(SupplyUsage supply in supplyUsage)
@@ -1581,6 +1600,60 @@ namespace CarCareTracker.Controllers
}
return Json(result);
}
[HttpPost]
public IActionResult SavePlanRecordTemplateToVehicleId(PlanRecordInput planRecord)
{
//check if template name already taken.
var existingRecord = _planRecordTemplateDataAccess.GetPlanRecordTemplatesByVehicleId(planRecord.VehicleId).Where(x=>x.Description == planRecord.Description).Any();
if (existingRecord)
{
return Json(new OperationResponse { Success = false, Message = "A template with that description already exists for this vehicle"});
}
planRecord.Files = planRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
var result = _planRecordTemplateDataAccess.SavePlanRecordTemplateToVehicle(planRecord);
return Json(new OperationResponse { Success = result, Message = result ? "Template Added" : StaticHelper.GenericErrorMessage });
}
[TypeFilter(typeof(CollaboratorFilter))]
[HttpGet]
public IActionResult GetPlanRecordTemplatesForVehicleId(int vehicleId)
{
var result = _planRecordTemplateDataAccess.GetPlanRecordTemplatesByVehicleId(vehicleId);
return PartialView("_PlanRecordTemplateModal", result);
}
[HttpPost]
public IActionResult DeletePlanRecordTemplateById(int planRecordTemplateId)
{
var result = _planRecordTemplateDataAccess.DeletePlanRecordTemplateById(planRecordTemplateId);
return Json(result);
}
[HttpPost]
public IActionResult ConvertPlanRecordTemplateToPlanRecord(int planRecordTemplateId)
{
var existingRecord = _planRecordTemplateDataAccess.GetPlanRecordTemplateById(planRecordTemplateId);
if (existingRecord.Id == default)
{
return Json(new OperationResponse { Success = false, Message = "Unable to find template" });
}
if (existingRecord.Supplies.Any())
{
//check if all supplies are available
var supplyAvailability = CheckSupplyRecordsAvailability(existingRecord.Supplies);
if (supplyAvailability.Any())
{
return Json(new OperationResponse { Success = false, Message = string.Join("<br>", supplyAvailability) });
}
}
//populate createdDate
existingRecord.DateCreated = DateTime.Now.ToString("G");
existingRecord.DateModified = DateTime.Now.ToString("G");
existingRecord.Id = default;
var result = _planRecordDataAccess.SavePlanRecordToVehicle(existingRecord.ToPlanRecord());
if (result && existingRecord.Supplies.Any())
{
RequisitionSupplyRecordsByUsage(existingRecord.Supplies);
}
return Json(new OperationResponse { Success = result, Message = result ? "Plan Record Added" : StaticHelper.GenericErrorMessage });
}
[HttpGet]
public IActionResult GetAddPlanRecordPartialView()
{

View File

@@ -0,0 +1,57 @@
using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper;
using CarCareTracker.Models;
using LiteDB;
namespace CarCareTracker.External.Implementations
{
public class PlanRecordTemplateDataAccess : IPlanRecordTemplateDataAccess
{
private static string dbName = StaticHelper.DbName;
private static string tableName = "planrecordtemplates";
public List<PlanRecordInput> GetPlanRecordTemplatesByVehicleId(int vehicleId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<PlanRecordInput>(tableName);
var planRecords = table.Find(Query.EQ(nameof(PlanRecordInput.VehicleId), vehicleId));
return planRecords.ToList() ?? new List<PlanRecordInput>();
};
}
public PlanRecordInput GetPlanRecordTemplateById(int planRecordId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<PlanRecordInput>(tableName);
return table.FindById(planRecordId);
};
}
public bool DeletePlanRecordTemplateById(int planRecordId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<PlanRecordInput>(tableName);
table.Delete(planRecordId);
return true;
};
}
public bool SavePlanRecordTemplateToVehicle(PlanRecordInput planRecord)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<PlanRecordInput>(tableName);
table.Upsert(planRecord);
return true;
};
}
public bool DeleteAllPlanRecordTemplatesByVehicleId(int vehicleId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<PlanRecord>(tableName);
var planRecords = table.DeleteMany(Query.EQ(nameof(PlanRecordInput.VehicleId), vehicleId));
return true;
};
}
}
}

View File

@@ -0,0 +1,13 @@
using CarCareTracker.Models;
namespace CarCareTracker.External.Interfaces
{
public interface IPlanRecordTemplateDataAccess
{
public List<PlanRecordInput> GetPlanRecordTemplatesByVehicleId(int vehicleId);
public PlanRecordInput GetPlanRecordTemplateById(int planRecordId);
public bool DeletePlanRecordTemplateById(int planRecordId);
public bool SavePlanRecordTemplateToVehicle(PlanRecordInput planRecord);
public bool DeleteAllPlanRecordTemplatesByVehicleId(int vehicleId);
}
}

View File

@@ -10,6 +10,7 @@ namespace CarCareTracker.Helper
UserConfig GetUserConfig(ClaimsPrincipal user);
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
string GetLogoUrl();
string GetServerLanguage();
public bool DeleteUserConfig(int userId);
}
public class ConfigHelper : IConfigHelper
@@ -34,6 +35,11 @@ namespace CarCareTracker.Helper
}
return logoUrl;
}
public string GetServerLanguage()
{
var serverLanguage = _config[nameof(UserConfig.UserLanguage)] ?? "en_US";
return serverLanguage;
}
public bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData)
{
var storedUserId = user.FindFirstValue(ClaimTypes.NameIdentifier);

View File

@@ -13,11 +13,14 @@ namespace CarCareTracker.Helper
public class MailHelper : IMailHelper
{
private readonly MailConfig mailConfig;
private readonly IFileHelper _fileHelper;
public MailHelper(
IConfiguration config
IConfiguration config,
IFileHelper fileHelper
) {
//load mailConfig from Configuration
mailConfig = config.GetSection("MailConfig").Get<MailConfig>();
_fileHelper = fileHelper;
}
public OperationResponse NotifyUserForRegistration(string emailAddress, string token)
{
@@ -75,14 +78,19 @@ namespace CarCareTracker.Helper
{
return new OperationResponse { Success = false, Message = "No reminders could be found" };
}
//get email template, this file has to exist since it's a static file.
var emailTemplatePath = _fileHelper.GetFullFilePath(StaticHelper.ReminderEmailTemplate);
string emailSubject = $"Vehicle Reminders From LubeLogger - {DateTime.Now.ToShortDateString()}";
//construct html table.
string emailBody = $"<h4>{vehicle.Year} {vehicle.Make} {vehicle.Model} #{vehicle.LicensePlate}</h4><br /><table style='width:100%'><tr><th style='padding:8px;'>Urgency</th><th style='padding:8px;'>Description</th></tr>";
string emailBody = File.ReadAllText(emailTemplatePath);
emailBody = emailBody.Replace("{VehicleInformation}", $"{vehicle.Year} {vehicle.Make} {vehicle.Model} #{vehicle.LicensePlate}");
string tableBody = "";
foreach(ReminderRecordViewModel reminder in reminders)
{
emailBody += $"<tr><td style='padding:8px; text-align:center;'>{reminder.Urgency}</td><td style='padding:8px; text-align:center;'>{reminder.Description}</td></tr>";
var dueOn = reminder.Metric == ReminderMetric.Both ? $"{reminder.Date} or {reminder.Mileage}" : reminder.Metric == ReminderMetric.Date ? $"{reminder.Date.ToShortDateString()}" : $"{reminder.Mileage}";
tableBody += $"<tr class='{reminder.Urgency}'><td>{StaticHelper.GetTitleCaseReminderUrgency(reminder.Urgency)}</td><td>{reminder.Description}</td><td>{dueOn}</td></tr>";
}
emailBody += "</table>";
emailBody = emailBody.Replace("{TableBody}", tableBody);
try
{
foreach (string emailAddress in emailAddresses)

View File

@@ -11,6 +11,22 @@ namespace CarCareTracker.Helper
public static string DbName = "data/cartracker.db";
public static string UserConfigPath = "config/userConfig.json";
public static string GenericErrorMessage = "An error occurred, please try again later";
public static string ReminderEmailTemplate = "defaults/reminderemailtemplate.txt";
public static string GetTitleCaseReminderUrgency(ReminderUrgency input)
{
switch (input)
{
case ReminderUrgency.NotUrgent:
return "Not Urgent";
case ReminderUrgency.VeryUrgent:
return "Very Urgent";
case ReminderUrgency.PastDue:
return "Past Due";
default:
return input.ToString();
}
}
public static string TruncateStrings(string input, int maxLength = 25)
{

View File

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

View File

@@ -8,5 +8,6 @@
public string NoteText { get; set; }
public bool Pinned { get; set; }
public List<string> Tags { get; set; } = new List<string>();
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
}
}

View File

@@ -5,6 +5,8 @@ using CarCareTracker.Logic;
using CarCareTracker.Middleware;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args);
@@ -24,6 +26,7 @@ builder.Services.AddSingleton<IUserAccessDataAccess, UserAccessDataAccess>();
builder.Services.AddSingleton<IUserConfigDataAccess, UserConfigDataAccess>();
builder.Services.AddSingleton<ISupplyRecordDataAccess, SupplyRecordDataAccess>();
builder.Services.AddSingleton<IPlanRecordDataAccess, PlanRecordDataAccess>();
builder.Services.AddSingleton<IPlanRecordTemplateDataAccess, PlanRecordTemplateDataAccess>();
builder.Services.AddSingleton<IOdometerRecordDataAccess, OdometerRecordDataAccess>();
//configure helpers
@@ -55,6 +58,17 @@ builder.Services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes("AuthN").RequireAuthenticatedUser().Build();
});
//Configure max file upload size
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = int.MaxValue; // if don't set default value is: 30 MB
});
builder.Services.Configure<FormOptions>(options =>
{
options.ValueLengthLimit = int.MaxValue;
options.MultipartBodyLengthLimit = int.MaxValue; // if don't set default value is: 128 MB
options.MultipartHeadersLengthLimit = int.MaxValue;
});
var app = builder.Build();

View File

@@ -187,7 +187,7 @@
<img src="/defaults/lubelogger_logo.png" />
</div>
<div class="d-flex justify-content-center">
<small class="text-body-secondary">Version 1.1.1</small>
<small class="text-body-secondary">Version 1.1.4</small>
</div>
<p class="lead">
Proudly developed in the rural town of Price, Utah by Hargata Softworks.

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +1,8 @@
<h1>Access Denied</h1>
<div class="row">
<div class="col-1">
<a href="/Home" class="btn btn-secondary btn-md mt-1 mb-1"><i class="bi bi-arrow-left-square"></i></a>
</div>
<div class="col-11">
<h1>Access Denied</h1>
</div>
</div>

View File

@@ -1,6 +1,7 @@
@using CarCareTracker.Helper
<!DOCTYPE html>
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var useDarkMode = userConfig.UseDarkMode;
@@ -10,6 +11,7 @@
var useThreeDecimals = userConfig.UseThreeDecimalGasCost;
var shortDatePattern = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
var numberFormat = System.Globalization.CultureInfo.CurrentCulture.NumberFormat;
var userLanguage = userConfig.UserLanguage;
shortDatePattern = shortDatePattern.ToLower();
if (!shortDatePattern.Contains("dd"))
{
@@ -86,7 +88,7 @@
return input;
}
function genericErrorMessage(){
return "An error has occurred, please try again later";
return decodeHTMLEntities('@translator.Translate(userLanguage, "An error has occurred, please try again later")');
}
</script>
@await RenderSectionAsync("Scripts", required: false)

View File

@@ -101,9 +101,10 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2">@translator.Translate(userLanguage,"Date Refueled")</th>
<th scope="col" class="col-2">@($"{translator.Translate(userLanguage,"Odometer")}({distanceUnit})")</th>
<th scope="col" class="col-2">@($"{translator.Translate(userLanguage, "Odometer")}({distanceUnit})")</th>
<th scope="col" class="col-1" style="cursor:pointer;" onclick="toggleSort('gas-tab-pane', this)">@($"Δ({distanceUnit})")</th>
<th scope="col" class="col-2" data-gas="consumption" data-unit="@consumptionUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{translator.Translate(userLanguage,"Consumption")}({consumptionUnit})")</th>
<th scope="col" class="col-4" data-gas="fueleconomy" data-unit="@fuelEconomyUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{@translator.Translate(userLanguage,"Fuel Economy")}({fuelEconomyUnit})")</th>
<th scope="col" class="col-3" data-gas="fueleconomy" data-unit="@fuelEconomyUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{@translator.Translate(userLanguage,"Fuel Economy")}({fuelEconomyUnit})")</th>
<th scope="col" class="col-1" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage,"Cost")</th>
<th scope="col" class="col-1" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage,"Unit Cost")</th>
</tr>
@@ -113,9 +114,10 @@
{
<tr class="d-flex" style="cursor:pointer;" onclick="showEditGasRecordModal(@gasRecord.Id)" data-tags='@string.Join(" ", gasRecord.Tags)'>
<td class="col-2">@gasRecord.Date</td>
<td class="col-2" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage">@gasRecord.Mileage</td>
<td class="col-2" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage" data-gas-original="@gasRecord.Mileage">@gasRecord.Mileage</td>
<td class="col-1">@(gasRecord.DeltaMileage == default ? "---" : gasRecord.DeltaMileage)</td>
<td class="col-2" data-gas-type="consumption" data-gas-aggregate="@gasRecord.Gallons">@gasRecord.Gallons.ToString("F")</td>
<td class="col-4" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-3" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-1" data-record-type="cost">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td>
<td class="col-1" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td>
</tr>

View File

@@ -28,6 +28,24 @@
<label for="noteTextArea">@translator.Translate(userLanguage,"Notes")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea class="form-control vehicleNoteContainer" id="noteTextArea">@Model.NoteText</textarea>
</div>
<div class="col-12">
@if (Model.Files.Any())
{
<div>
@await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="serviceRecordFiles">@translator.Translate(userLanguage, "Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="noteFiles">
<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
</div>
}
else
{
<label for="serviceRecordFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="noteFiles">
<br />
<small class="text-body-secondary">@translator.Translate(userLanguage, "Max File Size: 28.6MB")</small>
}
</div>
<div class="col-12">
<label for="noteRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>
<select multiple class="form-select" id="noteRecordTag">
@@ -57,6 +75,14 @@
}
</div>
<script>
var uploadedFiles = [];
getUploadedFilesFromModel();
function getUploadedFilesFromModel() {
@foreach (UploadedFiles filesUploaded in Model.Files)
{
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
}
}
function getNoteModelData(){
return { id: @Model.Id}
}

View File

@@ -82,7 +82,16 @@
<button type="button" class="btn btn-secondary" onclick="hideAddPlanRecordModal()">@translator.Translate(userLanguage, "Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="savePlanRecordToVehicle()">@translator.Translate(userLanguage, "Add New Plan Record")</button>
<div class="btn-group">
<button onclick="savePlanRecordToVehicle()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add New Plan Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="savePlanRecordTemplate()">@translator.Translate(userLanguage, "Save as Template")</a></li>
<li><a class="dropdown-item" href="#" onclick="showPlanRecordTemplatesModal()">@translator.Translate(userLanguage, "View Templates")</a></li>
</ul>
</div>
}
else if (!isNew)
{

View File

@@ -0,0 +1,87 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
@model List<PlanRecordInput>
<div class="modal-header">
<h5 class="modal-title">@translator.Translate(userLanguage,"Select Template")</h5>
<button type="button" class="btn-close" onclick="hidePlanRecordTemplatesModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@if (Model.Any())
{
<div class="row">
<div class="col-12" style="max-height:50vh; overflow-y:auto;">
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-8">@translator.Translate(userLanguage,"Description")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Use")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Delete")</th>
</tr>
</thead>
<tbody>
@foreach (PlanRecordInput planRecordTemplate in Model)
{
<tr class="d-flex" id="supplyRows">
<td class="col-8 text-truncate">
@StaticHelper.TruncateStrings(planRecordTemplate.Description)
@if (planRecordTemplate.Files.Any())
{
<i class="bi bi-paperclip ms-2"></i>
}
@if (planRecordTemplate.Supplies.Any())
{
<i class="bi bi-shop ms-2"></i>
}
@if (planRecordTemplate.ImportMode == ImportMode.ServiceRecord)
{
<i class="bi bi-card-checklist ms-2"></i>
}
else if (planRecordTemplate.ImportMode == ImportMode.UpgradeRecord)
{
<i class="bi bi-wrench-adjustable ms-2"></i>
}
else if (planRecordTemplate.ImportMode == ImportMode.RepairRecord)
{
<i class="bi bi-exclamation-octagon ms-2"></i>
}
@if (planRecordTemplate.Priority == PlanPriority.Critical)
{
<i class="bi bi-fire ms-2"></i>
}
else if (planRecordTemplate.Priority == PlanPriority.Normal)
{
<i class="bi bi-water ms-2"></i>
}
else if (planRecordTemplate.Priority == PlanPriority.Low)
{
<i class="bi bi-snow ms-2"></i>
}
</td>
<td class="col-2"><button type="button" class="btn btn-primary" onclick="usePlannerRecordTemplate(@planRecordTemplate.Id)"><i class="bi bi-plus-square"></i></button></td>
<td class="col-2"><button type="button" class="btn btn-danger" onclick="deletePlannerRecordTemplate(@planRecordTemplate.Id)"><i class="bi bi-trash"></i></button></td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
else
{
<div class="row">
<div class="col-12">
<div class="text-center">
<h4>@translator.Translate(userLanguage, "No templates are found.")</h4>
</div>
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hidePlanRecordTemplatesModal()">@translator.Translate(userLanguage, "Cancel")</button>
</div>

View File

@@ -101,3 +101,10 @@
</div>
</div>
</div>
<div class="modal fade" data-bs-focus="false" id="planRecordTemplateModal" tabindex="-1" role="dialog" aria-hidden="true" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content" id="planRecordTemplateModalContent">
</div>
</div>
</div>

View File

@@ -16,10 +16,15 @@
new Chart($("#donut-chart"), {
type: 'doughnut',
data: {
labels: ['@translator.Translate(userLanguage,"Not Urgent")', '@translator.Translate(userLanguage,"Urgent")', '@translator.Translate(userLanguage,"Very Urgent")', '@translator.Translate(userLanguage,"Past Due")'],
labels: [
decodeHTMLEntities('@translator.Translate(userLanguage, "Not Urgent")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Urgent")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Very Urgent")'),
decodeHTMLEntities('@translator.Translate(userLanguage, "Past Due")')
],
datasets: [
{
label: '@translator.Translate(userLanguage,"Reminders by Category")',
label: decodeHTMLEntities('@translator.Translate(userLanguage, "Reminders by Category")'),
backgroundColor: ["#488f31", "#ffa600", "#de425b", "#cccccc"],
data: [
@Model.NotUrgentCount,
@@ -40,7 +45,7 @@
},
title: {
display: true,
text: '@translator.Translate(userLanguage,"Reminders by Urgency")',
text: decodeHTMLEntities('@translator.Translate(userLanguage, "Reminders by Urgency")'),
color: useDarkMode ? "#fff" : "#000"
},
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,49 @@
<html>
<head>
<style>
body {
font-family: arial, sans-serif;
}
table {
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
.VeryUrgent {
background-color: #EA9999;
}
.Urgent {
background-color: #FFE599;
}
.NotUrgent {
background-color: #B6D7A8;
}
.PastDue {
background-color: #CCCCCC;
}
</style>
</head>
<body>
<h2>{VehicleInformation}</h2>
<table>
<tr>
<th>
Urgency
</th>
<th>
Description
</th>
<th>
Due
</th>
</tr>
{TableBody}
</table>
</body>
</html>

View File

@@ -257,28 +257,28 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
convertedAmount = 100 / convertedAmount;
elem.innerText = convertedAmount.toFixed(2);
}
//update labels up top.
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) {
newAverage = 100 / newAverage;
var averageLabel = $("#averageFuelMileageLabel");
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${newAverage.toFixed(2)}`);
}
var newMin = globalParseFloat($("#minFuelMileageLabel").text().split(":")[1].trim());
if (newMin > 0) {
newMin = 100 / newMin;
var minLabel = $("#minFuelMileageLabel");
minLabel.text(`${minLabel.text().split(':')[0]}: ${newMin.toFixed(2)}`);
}
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().split(":")[1].trim());
if (newMax > 0) {
newMax = 100 / newMax;
var maxLabel = $("#maxFuelMileageLabel");
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${newMax.toFixed(2)}`);
}
sender.text(sender.text().replace(sender.attr("data-unit"), "km/l"));
sender.attr("data-unit", "km/l");
});
//update labels up top.
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) {
newAverage = 100 / newAverage;
var averageLabel = $("#averageFuelMileageLabel");
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${newAverage.toFixed(2)}`);
}
var newMin = globalParseFloat($("#minFuelMileageLabel").text().split(":")[1].trim());
if (newMin > 0) {
newMin = 100 / newMin;
var minLabel = $("#minFuelMileageLabel");
minLabel.text(`${minLabel.text().split(':')[0]}: ${newMin.toFixed(2)}`);
}
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().split(":")[1].trim());
if (newMax > 0) {
newMax = 100 / newMax;
var maxLabel = $("#maxFuelMileageLabel");
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${newMax.toFixed(2)}`);
}
sender.text(sender.text().replace(sender.attr("data-unit"), "km/l"));
sender.attr("data-unit", "km/l");
if (save) { setDebounce(saveUserGasTabPreferences); }
break;
}
@@ -291,27 +291,28 @@ function convertFuelMileageUnits(currentUnit, destinationUnit, save) {
convertedAmount = 100 / convertedAmount;
elem.innerText = convertedAmount.toFixed(2);
}
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) {
newAverage = 100 / newAverage;
var averageLabel = $("#averageFuelMileageLabel");
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${newAverage.toFixed(2)}`);
}
var newMin = globalParseFloat($("#minFuelMileageLabel").text().split(":")[1].trim());
if (newMin > 0) {
newMin = 100 / newMin;
var minLabel = $("#minFuelMileageLabel");
minLabel.text(`${minLabel.text().split(':')[0]}: ${newMin.toFixed(2)}`);
}
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().split(":")[1].trim());
if (newMax > 0) {
newMax = 100 / newMax;
var maxLabel = $("#maxFuelMileageLabel");
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${newMax.toFixed(2)}`);
}
sender.text(sender.text().replace(sender.attr("data-unit"), "l/100km"));
sender.attr("data-unit", "l/100km");
});
var newAverage = globalParseFloat($("#averageFuelMileageLabel").text().split(":")[1].trim());
if (newAverage > 0) {
newAverage = 100 / newAverage;
var averageLabel = $("#averageFuelMileageLabel");
averageLabel.text(`${averageLabel.text().split(':')[0]}: ${newAverage.toFixed(2)}`);
}
var newMin = globalParseFloat($("#minFuelMileageLabel").text().split(":")[1].trim());
if (newMin > 0) {
newMin = 100 / newMin;
var minLabel = $("#minFuelMileageLabel");
minLabel.text(`${minLabel.text().split(':')[0]}: ${newMin.toFixed(2)}`);
}
var newMax = globalParseFloat($("#maxFuelMileageLabel").text().split(":")[1].trim());
if (newMax > 0) {
newMax = 100 / newMax;
var maxLabel = $("#maxFuelMileageLabel");
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${newMax.toFixed(2)}`);
}
sender.text(sender.text().replace(sender.attr("data-unit"), "l/100km"));
sender.attr("data-unit", "l/100km");
if (save) { setDebounce(saveUserGasTabPreferences); }
break;
}
@@ -341,8 +342,14 @@ function updateMPGLabels() {
} else {
averageLabel.text(`${averageLabel.text().split(':')[0]}: 0.00`);
}
minLabel.text(`${minLabel.text().split(':')[0]}: ${minMPG.toFixed(2)}`);
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${maxMPG.toFixed(2)}`);
if (!getGlobalConfig().useMPG && $("[data-gas='fueleconomy']").attr("data-unit") != 'km/l') {
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${minMPG.toFixed(2)}`);
minLabel.text(`${minLabel.text().split(':')[0]}: ${maxMPG.toFixed(2)}`);
}
else {
minLabel.text(`${minLabel.text().split(':')[0]}: ${minMPG.toFixed(2)}`);
maxLabel.text(`${maxLabel.text().split(':')[0]}: ${maxMPG.toFixed(2)}`);
}
}
}

View File

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

View File

@@ -96,6 +96,7 @@ function getAndValidateNoteValues() {
vehicleId: vehicleId,
description: noteDescription,
noteText: noteText,
files: uploadedFiles,
pinned: noteIsPinned,
tags: noteTags
}

View File

@@ -74,6 +74,79 @@ function savePlanRecordToVehicle(isEdit) {
}
})
}
function showPlanRecordTemplatesModal() {
var vehicleId = GetVehicleId().vehicleId;
$.get(`/Vehicle/GetPlanRecordTemplatesForVehicleId?vehicleId=${vehicleId}`, function (data) {
if (data) {
$("#planRecordTemplateModalContent").html(data);
hideAddPlanRecordModal();
$('#planRecordTemplateModal').modal('show');
}
});
}
function hidePlanRecordTemplatesModal() {
$('#planRecordTemplateModal').modal('hide');
$('#planRecordModal').modal('show');
}
function usePlannerRecordTemplate(planRecordTemplateId) {
$.post(`/Vehicle/ConvertPlanRecordTemplateToPlanRecord?planRecordTemplateId=${planRecordTemplateId}`, function (data) {
if (data.success) {
var vehicleId = GetVehicleId().vehicleId;
successToast(data.message);
$('#planRecordTemplateModal').modal('hide');
hideAddPlanRecordModal();
saveScrollPosition();
getVehiclePlanRecords(vehicleId);
} else {
errorToast(data.message);
}
});
}
function deletePlannerRecordTemplate(planRecordTemplateId) {
$("#workAroundInput").show();
Swal.fire({
title: "Confirm Deletion?",
text: "Deleted Plan Templates cannot be restored.",
showCancelButton: true,
confirmButtonText: "Delete",
confirmButtonColor: "#dc3545"
}).then((result) => {
if (result.isConfirmed) {
$.post(`/Vehicle/DeletePlanRecordTemplateById?planRecordTemplateId=${planRecordTemplateId}`, function (data) {
$("#workAroundInput").hide();
if (data) {
successToast("Template Deleted");
hidePlanRecordTemplatesModal();
} else {
errorToast(genericErrorMessage());
}
});
} else {
$("#workAroundInput").hide();
}
});
}
function savePlanRecordTemplate() {
//get values
var formValues = getAndValidatePlanRecordValues();
//validate
if (formValues.hasError) {
errorToast("Please check the form data");
return;
}
//save to db.
$.post('/Vehicle/SavePlanRecordTemplateToVehicleId', { planRecord: formValues }, function (data) {
if (data.success) {
successToast(data.message);
hideAddPlanRecordModal();
saveScrollPosition();
getVehiclePlanRecords(formValues.vehicleId);
} else {
errorToast(data.message);
}
})
}
function getAndValidatePlanRecordValues() {
var planDescription = $("#planRecordDescription").val();
var planCost = $("#planRecordCost").val();