Compare commits

...

28 Commits

Author SHA1 Message Date
Hargata Softworks
920de489be Merge pull request #372 from hargata/Hargata/delta.stats
Added MOTD.
2024-03-07 08:46:14 -07:00
DESKTOP-GENO133\IvanPlex
19cb964543 Added MOTD. 2024-03-07 08:44:58 -07:00
Hargata Softworks
eb03979ad8 Merge pull request #369 from hargata/Hargata/delta.stats
Added Total and Average Cost
2024-03-06 07:44:28 -07:00
DESKTOP-GENO133\IvanPlex
ebe871b82a reduced operations 2024-03-06 07:42:46 -07:00
DESKTOP-GENO133\IvanPlex
04a103fdc0 column specific search 2024-03-06 07:40:18 -07:00
DESKTOP-GENO133\IvanPlex
d0f80d4150 Added Total and Average Cost 2024-03-05 08:43:10 -07:00
Hargata Softworks
48c388dea7 Merge pull request #368 from redge76/Sample_postgreSQL
Sample postgreSQL configuration
2024-03-04 17:13:02 -07:00
Redge
d05f8b1f84 Sample postgrSQL connection stringin file .env
add a sample connection string to enable postgrSQL database backend
2024-03-05 01:09:10 +01:00
Redge
167d6e607e Sample docker-compose.postgresql.yml 2024-03-05 01:05:24 +01:00
Hargata Softworks
8755c6379a Merge pull request #367 from hargata/Hargata/record.delta.stats
Hargata/record.delta.stats
2024-03-04 16:20:24 -07:00
DESKTOP-GENO133\IvanPlex
1e2bd0ea4b Allow users to set their own reminder urgency threshold 2024-03-04 16:18:09 -07:00
DESKTOP-GENO133\IvanPlex
2cf93cf669 Added setting to hide sold vehicles. 2024-03-04 13:37:07 -07:00
Hargata Softworks
a18b81d52e Merge pull request #363 from hargata/Hargata/record.delta.stats
Hargata/record.delta.stats
2024-03-03 21:10:14 -07:00
DESKTOP-GENO133\IvanPlex
604c98a031 allowed users to configure their own file extensions. 2024-03-03 19:32:20 -07:00
DESKTOP-GENO133\IvanPlex
940f48b816 Updated version number 2024-03-03 09:20:02 -07:00
DESKTOP-GENO133\IvanPlex
f81545178d Added statistics between records. 2024-03-03 09:09:42 -07:00
Hargata Softworks
a87b599bdf Merge pull request #362 from hargata/Hargata/persist.columns
null check for column preferences
2024-03-02 19:13:11 -07:00
DESKTOP-GENO133\IvanPlex
2c45521fb4 null check for column preferences 2024-03-02 19:11:21 -07:00
Hargata Softworks
0870387995 Merge pull request #361 from hargata/Hargata/persist.columns
Allows users to inject root user credentials as environment variables.
2024-03-02 07:53:48 -07:00
DESKTOP-GENO133\IvanPlex
be281c78ff Allows users to inject root user credentials as environment variables. 2024-03-02 07:51:37 -07:00
Hargata Softworks
87c54335db Merge pull request #360 from hargata/Hargata/persist.columns
authenticate root user via configHelper
2024-03-02 07:13:43 -07:00
DESKTOP-GENO133\IvanPlex
12e6c1677b null checks 2024-03-02 07:13:13 -07:00
DESKTOP-GENO133\IvanPlex
f69b789346 authenticate root user via configHelper 2024-03-02 07:11:54 -07:00
Hargata Softworks
9f05295f18 Merge pull request #359 from hargata/Hargata/persist.columns
revamped column check toggle behavior
2024-03-02 06:50:52 -07:00
DESKTOP-GENO133\IvanPlex
bf14e4c8c0 added functonality to persist column visibility 2024-03-02 06:49:40 -07:00
Hargata Softworks
bd4db09637 Merge pull request #357 from hargata/Hargata/responsive.table.columns
allow table columns to grow as needed.
2024-03-02 05:48:23 -07:00
DESKTOP-GENO133\IvanPlex
4b5dcb1c7e revamped column check toggle behavior 2024-03-02 05:47:22 -07:00
DESKTOP-GENO133\IvanPlex
b7f1ac5e8f allow table columns to grow as needed. 2024-03-02 04:57:45 -07:00
37 changed files with 594 additions and 234 deletions

6
.env
View File

@@ -6,4 +6,8 @@ MailConfig__UseSSL="false"
MailConfig__Port=587 MailConfig__Port=587
MailConfig__Username="" MailConfig__Username=""
MailConfig__Password="" MailConfig__Password=""
LOGGING__LOGLEVEL__DEFAULT=Error LOGGING__LOGLEVEL__DEFAULT=Error
# * Uncoment this line if you use postgresSQL as database backend.
# * Check the docker-compose.postgresql.yml file
#POSTGRES_CONNECTION="Host=postgres;Username=lubelogger;Password=lubepass;Database=lubelogger;"

View File

@@ -73,8 +73,7 @@ namespace CarCareTracker.Controllers
//we don't care about mileages so we can basically fake the current vehicle mileage. //we don't care about mileages so we can basically fake the current vehicle mileage.
if (vehicleReminders.Any()) if (vehicleReminders.Any())
{ {
var maxMileage = vehicleReminders.Max(x => x.Mileage) + 1000; var reminderUrgency = _reminderHelper.GetReminderRecordViewModels(vehicleReminders, 0, DateTime.Now);
var reminderUrgency = _reminderHelper.GetReminderRecordViewModels(vehicleReminders, maxMileage, DateTime.Now);
reminderUrgency = reminderUrgency.Select(x => new ReminderRecordViewModel { Id = x.Id, Date = x.Date, Urgency = x.Urgency, Description = $"{vehicle.Year} {vehicle.Make} {vehicle.Model} #{vehicle.LicensePlate} - {x.Description}" }).ToList(); reminderUrgency = reminderUrgency.Select(x => new ReminderRecordViewModel { Id = x.Id, Date = x.Date, Urgency = x.Urgency, Description = $"{vehicle.Year} {vehicle.Make} {vehicle.Model} #{vehicle.LicensePlate} - {x.Description}" }).ToList();
reminders.AddRange(reminderUrgency); reminders.AddRange(reminderUrgency);
} }
@@ -84,7 +83,7 @@ namespace CarCareTracker.Controllers
public IActionResult ViewCalendarReminder(int reminderId) public IActionResult ViewCalendarReminder(int reminderId)
{ {
var reminder = _reminderRecordDataAccess.GetReminderRecordById(reminderId); var reminder = _reminderRecordDataAccess.GetReminderRecordById(reminderId);
var reminderUrgency = _reminderHelper.GetReminderRecordViewModels(new List<ReminderRecord> { reminder }, reminder.Mileage + 1000, DateTime.Now).FirstOrDefault(); var reminderUrgency = _reminderHelper.GetReminderRecordViewModels(new List<ReminderRecord> { reminder }, 0, DateTime.Now).FirstOrDefault();
return PartialView("_ReminderRecordCalendarModal", reminderUrgency); return PartialView("_ReminderRecordCalendarModal", reminderUrgency);
} }
public IActionResult Settings() public IActionResult Settings()
@@ -101,9 +100,23 @@ namespace CarCareTracker.Controllers
[HttpPost] [HttpPost]
public IActionResult WriteToSettings(UserConfig userConfig) public IActionResult WriteToSettings(UserConfig userConfig)
{ {
//retrieve existing userConfig.
var existingConfig = _config.GetUserConfig(User);
//copy over stuff that persists
userConfig.UserColumnPreferences = existingConfig.UserColumnPreferences;
userConfig.ReminderUrgencyConfig = existingConfig.ReminderUrgencyConfig;
var result = _config.SaveUserConfig(User, userConfig); var result = _config.SaveUserConfig(User, userConfig);
return Json(result); return Json(result);
} }
[HttpPost]
public IActionResult SaveReminderUrgencyThreshold(ReminderUrgencyConfig reminderUrgencyConfig)
{
//retrieve existing userConfig.
var existingConfig = _config.GetUserConfig(User);
existingConfig.ReminderUrgencyConfig = reminderUrgencyConfig;
var result = _config.SaveUserConfig(User, existingConfig);
return Json(result);
}
public IActionResult Privacy() public IActionResult Privacy()
{ {
return View(); return View();

View File

@@ -2448,7 +2448,29 @@ namespace CarCareTracker.Controllers
} }
return Json(result); return Json(result);
} }
[HttpPost]
public IActionResult SaveUserColumnPreferences(UserColumnPreference columnPreference)
{
try
{
var userConfig = _config.GetUserConfig(User);
var existingUserColumnPreference = userConfig.UserColumnPreferences.Where(x => x.Tab == columnPreference.Tab);
if (existingUserColumnPreference.Any())
{
var existingPreference = existingUserColumnPreference.Single();
existingPreference.VisibleColumns = columnPreference.VisibleColumns;
} else
{
userConfig.UserColumnPreferences.Add(columnPreference);
}
var result = _config.SaveUserConfig(User, userConfig);
return Json(result);
} catch (Exception ex)
{
_logger.LogError(ex.Message);
return Json(false);
}
}
#endregion #endregion
} }
} }

View File

@@ -8,12 +8,16 @@ namespace CarCareTracker.Helper
public interface IConfigHelper public interface IConfigHelper
{ {
OpenIDConfig GetOpenIDConfig(); OpenIDConfig GetOpenIDConfig();
ReminderUrgencyConfig GetReminderUrgencyConfig();
UserConfig GetUserConfig(ClaimsPrincipal user); UserConfig GetUserConfig(ClaimsPrincipal user);
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData); bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
bool AuthenticateRootUser(string username, string password);
string GetMOTD();
string GetLogoUrl(); string GetLogoUrl();
string GetServerLanguage(); string GetServerLanguage();
bool GetServerEnableShopSupplies(); bool GetServerEnableShopSupplies();
string GetServerPostgresConnection(); string GetServerPostgresConnection();
string GetAllowedFileUploadExtensions();
public bool DeleteUserConfig(int userId); public bool DeleteUserConfig(int userId);
} }
public class ConfigHelper : IConfigHelper public class ConfigHelper : IConfigHelper
@@ -29,11 +33,25 @@ namespace CarCareTracker.Helper
_userConfig = userConfig; _userConfig = userConfig;
_cache = memoryCache; _cache = memoryCache;
} }
public string GetMOTD()
{
var motd = _config["LUBELOGGER_MOTD"];
if (string.IsNullOrWhiteSpace(motd))
{
motd = "";
}
return motd;
}
public OpenIDConfig GetOpenIDConfig() public OpenIDConfig GetOpenIDConfig()
{ {
OpenIDConfig openIdConfig = _config.GetSection("OpenIDConfig").Get<OpenIDConfig>() ?? new OpenIDConfig(); OpenIDConfig openIdConfig = _config.GetSection("OpenIDConfig").Get<OpenIDConfig>() ?? new OpenIDConfig();
return openIdConfig; return openIdConfig;
} }
public ReminderUrgencyConfig GetReminderUrgencyConfig()
{
ReminderUrgencyConfig reminderUrgencyConfig = _config.GetSection("ReminderUrgencyConfig").Get<ReminderUrgencyConfig>() ?? new ReminderUrgencyConfig();
return reminderUrgencyConfig;
}
public string GetLogoUrl() public string GetLogoUrl()
{ {
var logoUrl = _config["LUBELOGGER_LOGO_URL"]; var logoUrl = _config["LUBELOGGER_LOGO_URL"];
@@ -43,6 +61,24 @@ namespace CarCareTracker.Helper
} }
return logoUrl; return logoUrl;
} }
public string GetAllowedFileUploadExtensions()
{
var allowedFileExtensions = _config["LUBELOGGER_ALLOWED_FILE_EXTENSIONS"];
if (string.IsNullOrWhiteSpace(allowedFileExtensions)){
return StaticHelper.DefaultAllowedFileExtensions;
}
return allowedFileExtensions;
}
public bool AuthenticateRootUser(string username, string password)
{
var rootUsername = _config[nameof(UserConfig.UserNameHash)] ?? string.Empty;
var rootPassword = _config[nameof(UserConfig.UserPasswordHash)] ?? string.Empty;
if (string.IsNullOrWhiteSpace(rootUsername) || string.IsNullOrWhiteSpace(rootPassword))
{
return false;
}
return username == rootUsername && password == rootPassword;
}
public string GetServerLanguage() public string GetServerLanguage()
{ {
var serverLanguage = _config[nameof(UserConfig.UserLanguage)] ?? "en_US"; var serverLanguage = _config[nameof(UserConfig.UserLanguage)] ?? "en_US";
@@ -81,20 +117,9 @@ namespace CarCareTracker.Helper
File.WriteAllText(StaticHelper.UserConfigPath, System.Text.Json.JsonSerializer.Serialize(new UserConfig())); File.WriteAllText(StaticHelper.UserConfigPath, System.Text.Json.JsonSerializer.Serialize(new UserConfig()));
} }
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath); var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents); configData.EnableAuth = bool.Parse(_config[nameof(UserConfig.EnableAuth)] ?? "false");
if (existingUserConfig is not null) configData.UserNameHash = _config[nameof(UserConfig.UserNameHash)] ?? string.Empty;
{ configData.UserPasswordHash = _config[nameof(UserConfig.UserPasswordHash)] ?? string.Empty;
//copy over settings that are off limits on the settings page.
configData.EnableAuth = existingUserConfig.EnableAuth;
configData.UserNameHash = existingUserConfig.UserNameHash;
configData.UserPasswordHash = existingUserConfig.UserPasswordHash;
}
else
{
configData.EnableAuth = false;
configData.UserNameHash = string.Empty;
configData.UserPasswordHash = string.Empty;
}
File.WriteAllText(StaticHelper.UserConfigPath, System.Text.Json.JsonSerializer.Serialize(configData)); File.WriteAllText(StaticHelper.UserConfigPath, System.Text.Json.JsonSerializer.Serialize(configData));
_cache.Set<UserConfig>($"userConfig_{userId}", configData); _cache.Set<UserConfig>($"userConfig_{userId}", configData);
return true; return true;
@@ -139,9 +164,12 @@ namespace CarCareTracker.Helper
PreferredGasMileageUnit = _config[nameof(UserConfig.PreferredGasMileageUnit)], PreferredGasMileageUnit = _config[nameof(UserConfig.PreferredGasMileageUnit)],
PreferredGasUnit = _config[nameof(UserConfig.PreferredGasUnit)], PreferredGasUnit = _config[nameof(UserConfig.PreferredGasUnit)],
UserLanguage = _config[nameof(UserConfig.UserLanguage)], UserLanguage = _config[nameof(UserConfig.UserLanguage)],
HideSoldVehicles = bool.Parse(_config[nameof(UserConfig.HideSoldVehicles)]),
EnableShopSupplies = bool.Parse(_config[nameof(UserConfig.EnableShopSupplies)]), EnableShopSupplies = bool.Parse(_config[nameof(UserConfig.EnableShopSupplies)]),
EnableExtraFieldColumns = bool.Parse(_config[nameof(UserConfig.EnableExtraFieldColumns)]), EnableExtraFieldColumns = bool.Parse(_config[nameof(UserConfig.EnableExtraFieldColumns)]),
VisibleTabs = _config.GetSection("VisibleTabs").Get<List<ImportMode>>(), VisibleTabs = _config.GetSection(nameof(UserConfig.VisibleTabs)).Get<List<ImportMode>>(),
UserColumnPreferences = _config.GetSection(nameof(UserConfig.UserColumnPreferences)).Get<List<UserColumnPreference>>() ?? new List<UserColumnPreference>(),
ReminderUrgencyConfig = _config.GetSection(nameof(UserConfig.ReminderUrgencyConfig)).Get<ReminderUrgencyConfig>() ?? new ReminderUrgencyConfig(),
DefaultTab = (ImportMode)int.Parse(_config[nameof(UserConfig.DefaultTab)]) DefaultTab = (ImportMode)int.Parse(_config[nameof(UserConfig.DefaultTab)])
}; };
int userId = 0; int userId = 0;

View File

@@ -9,6 +9,11 @@ namespace CarCareTracker.Helper
} }
public class ReminderHelper: IReminderHelper public class ReminderHelper: IReminderHelper
{ {
private readonly IConfigHelper _config;
public ReminderHelper(IConfigHelper config)
{
_config = config;
}
public ReminderRecord GetUpdatedRecurringReminderRecord(ReminderRecord existingReminder) public ReminderRecord GetUpdatedRecurringReminderRecord(ReminderRecord existingReminder)
{ {
if (existingReminder.Metric == ReminderMetric.Both) if (existingReminder.Metric == ReminderMetric.Both)
@@ -56,6 +61,7 @@ namespace CarCareTracker.Helper
public List<ReminderRecordViewModel> GetReminderRecordViewModels(List<ReminderRecord> reminders, int currentMileage, DateTime dateCompare) public List<ReminderRecordViewModel> GetReminderRecordViewModels(List<ReminderRecord> reminders, int currentMileage, DateTime dateCompare)
{ {
List<ReminderRecordViewModel> reminderViewModels = new List<ReminderRecordViewModel>(); List<ReminderRecordViewModel> reminderViewModels = new List<ReminderRecordViewModel>();
var reminderUrgencyConfig = _config.GetReminderUrgencyConfig();
foreach (var reminder in reminders) foreach (var reminder in reminders)
{ {
var reminderViewModel = new ReminderRecordViewModel() var reminderViewModel = new ReminderRecordViewModel()
@@ -81,24 +87,24 @@ namespace CarCareTracker.Helper
reminderViewModel.Urgency = ReminderUrgency.PastDue; reminderViewModel.Urgency = ReminderUrgency.PastDue;
reminderViewModel.Metric = ReminderMetric.Odometer; reminderViewModel.Metric = ReminderMetric.Odometer;
} }
else if (reminder.Date < dateCompare.AddDays(7)) else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.VeryUrgentDays))
{ {
//if less than a week from today or less than 50 miles from current mileage then very urgent. //if less than a week from today or less than 50 miles from current mileage then very urgent.
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent; reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
//have to specify by which metric this reminder is urgent. //have to specify by which metric this reminder is urgent.
reminderViewModel.Metric = ReminderMetric.Date; reminderViewModel.Metric = ReminderMetric.Date;
} }
else if (reminder.Mileage < currentMileage + 50) else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.VeryUrgentDistance)
{ {
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent; reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
reminderViewModel.Metric = ReminderMetric.Odometer; reminderViewModel.Metric = ReminderMetric.Odometer;
} }
else if (reminder.Date < dateCompare.AddDays(30)) else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.UrgentDays))
{ {
reminderViewModel.Urgency = ReminderUrgency.Urgent; reminderViewModel.Urgency = ReminderUrgency.Urgent;
reminderViewModel.Metric = ReminderMetric.Date; reminderViewModel.Metric = ReminderMetric.Date;
} }
else if (reminder.Mileage < currentMileage + 100) else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.UrgentDistance)
{ {
reminderViewModel.Urgency = ReminderUrgency.Urgent; reminderViewModel.Urgency = ReminderUrgency.Urgent;
reminderViewModel.Metric = ReminderMetric.Odometer; reminderViewModel.Metric = ReminderMetric.Odometer;
@@ -110,11 +116,11 @@ namespace CarCareTracker.Helper
{ {
reminderViewModel.Urgency = ReminderUrgency.PastDue; reminderViewModel.Urgency = ReminderUrgency.PastDue;
} }
else if (reminder.Date < dateCompare.AddDays(7)) else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.VeryUrgentDays))
{ {
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent; reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
} }
else if (reminder.Date < dateCompare.AddDays(30)) else if (reminder.Date < dateCompare.AddDays(reminderUrgencyConfig.UrgentDays))
{ {
reminderViewModel.Urgency = ReminderUrgency.Urgent; reminderViewModel.Urgency = ReminderUrgency.Urgent;
} }
@@ -126,11 +132,11 @@ namespace CarCareTracker.Helper
reminderViewModel.Urgency = ReminderUrgency.PastDue; reminderViewModel.Urgency = ReminderUrgency.PastDue;
reminderViewModel.Metric = ReminderMetric.Odometer; reminderViewModel.Metric = ReminderMetric.Odometer;
} }
else if (reminder.Mileage < currentMileage + 50) else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.VeryUrgentDistance)
{ {
reminderViewModel.Urgency = ReminderUrgency.VeryUrgent; reminderViewModel.Urgency = ReminderUrgency.VeryUrgent;
} }
else if (reminder.Mileage < currentMileage + 100) else if (reminder.Mileage < currentMileage + reminderUrgencyConfig.UrgentDistance)
{ {
reminderViewModel.Urgency = ReminderUrgency.Urgent; reminderViewModel.Urgency = ReminderUrgency.Urgent;
} }

View File

@@ -8,10 +8,12 @@ namespace CarCareTracker.Helper
/// </summary> /// </summary>
public static class StaticHelper public static class StaticHelper
{ {
public static string VersionNumber = "1.2.5";
public static string DbName = "data/cartracker.db"; public static string DbName = "data/cartracker.db";
public static string UserConfigPath = "config/userConfig.json"; public static string UserConfigPath = "config/userConfig.json";
public static string GenericErrorMessage = "An error occurred, please try again later"; public static string GenericErrorMessage = "An error occurred, please try again later";
public static string ReminderEmailTemplate = "defaults/reminderemailtemplate.txt"; public static string ReminderEmailTemplate = "defaults/reminderemailtemplate.txt";
public static string DefaultAllowedFileExtensions = ".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx";
public static string GetTitleCaseReminderUrgency(ReminderUrgency input) public static string GetTitleCaseReminderUrgency(ReminderUrgency input)
{ {
@@ -225,5 +227,22 @@ namespace CarCareTracker.Helper
{ {
return new DateTimeOffset(date).ToUnixTimeMilliseconds(); return new DateTimeOffset(date).ToUnixTimeMilliseconds();
} }
public static void InitMessage(IConfiguration config)
{
Console.WriteLine($"LubeLogger {VersionNumber}");
Console.WriteLine("Website: https://lubelogger.com");
Console.WriteLine("Documentation: https://docs.lubelogger.com");
Console.WriteLine("GitHub: https://github.com/hargata/lubelog");
var mailConfig = config.GetSection("MailConfig").Get<MailConfig>();
if (mailConfig != null && !string.IsNullOrWhiteSpace(mailConfig.EmailServer))
{
Console.WriteLine($"SMTP Configured for {mailConfig.EmailServer}");
} else
{
Console.WriteLine("SMTP Not Configured");
}
var motd = config["LUBELOGGER_MOTD"] ?? "Not Configured";
Console.WriteLine($"Message Of The Day: {motd}");
}
} }
} }

View File

@@ -35,15 +35,18 @@ namespace CarCareTracker.Logic
private readonly IUserRecordDataAccess _userData; private readonly IUserRecordDataAccess _userData;
private readonly ITokenRecordDataAccess _tokenData; private readonly ITokenRecordDataAccess _tokenData;
private readonly IMailHelper _mailHelper; private readonly IMailHelper _mailHelper;
private readonly IConfigHelper _configHelper;
private IMemoryCache _cache; private IMemoryCache _cache;
public LoginLogic(IUserRecordDataAccess userData, public LoginLogic(IUserRecordDataAccess userData,
ITokenRecordDataAccess tokenData, ITokenRecordDataAccess tokenData,
IMailHelper mailHelper, IMailHelper mailHelper,
IConfigHelper configHelper,
IMemoryCache memoryCache) IMemoryCache memoryCache)
{ {
_userData = userData; _userData = userData;
_tokenData = tokenData; _tokenData = tokenData;
_mailHelper = mailHelper; _mailHelper = mailHelper;
_configHelper = configHelper;
_cache = memoryCache; _cache = memoryCache;
} }
public bool CheckIfUserIsValid(int userId) public bool CheckIfUserIsValid(int userId)
@@ -412,21 +415,9 @@ namespace CarCareTracker.Logic
} }
private bool UserIsRoot(LoginModel credentials) private bool UserIsRoot(LoginModel credentials)
{ {
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath); var hashedUserName = GetHash(credentials.UserName);
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents); var hashedPassword = GetHash(credentials.Password);
if (existingUserConfig is not null) return _configHelper.AuthenticateRootUser(hashedUserName, hashedPassword);
{
//create hashes of the login credentials.
var hashedUserName = GetHash(credentials.UserName);
var hashedPassword = GetHash(credentials.Password);
//compare against stored hash.
if (hashedUserName == existingUserConfig.UserNameHash &&
hashedPassword == existingUserConfig.UserPasswordHash)
{
return true;
}
}
return false;
} }
#endregion #endregion
private static string GetHash(string value) private static string GetHash(string value)

View File

@@ -0,0 +1,10 @@
namespace CarCareTracker.Models
{
public class ReminderUrgencyConfig
{
public int UrgentDays { get; set; } = 30;
public int VeryUrgentDays { get; set; } = 7;
public int UrgentDistance { get; set; } = 100;
public int VeryUrgentDistance { get; set; } = 50;
}
}

View File

@@ -0,0 +1,8 @@
namespace CarCareTracker.Models
{
public class UserColumnPreference
{
public ImportMode Tab { get; set; }
public List<string> VisibleColumns { get; set; } = new List<string>();
}
}

View File

@@ -15,8 +15,11 @@
public bool EnableAutoOdometerInsert { get; set; } public bool EnableAutoOdometerInsert { get; set; }
public bool EnableShopSupplies { get; set; } public bool EnableShopSupplies { get; set; }
public bool EnableExtraFieldColumns { get; set; } public bool EnableExtraFieldColumns { get; set; }
public bool HideSoldVehicles { get; set; }
public string PreferredGasUnit { get; set; } = string.Empty; public string PreferredGasUnit { get; set; } = string.Empty;
public string PreferredGasMileageUnit { get; set; } = string.Empty; public string PreferredGasMileageUnit { get; set; } = string.Empty;
public List<UserColumnPreference> UserColumnPreferences { get; set; } = new List<UserColumnPreference>();
public ReminderUrgencyConfig ReminderUrgencyConfig { get; set; } = new ReminderUrgencyConfig();
public string UserNameHash { get; set; } public string UserNameHash { get; set; }
public string UserPasswordHash { get; set;} public string UserPasswordHash { get; set;}
public string UserLanguage { get; set; } = "en_US"; public string UserLanguage { get; set; } = "en_US";

View File

@@ -10,6 +10,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
//Print Messages
StaticHelper.InitMessage(builder.Configuration);
// Add services to the container. // Add services to the container.
builder.Services.AddControllersWithViews(); builder.Services.AddControllersWithViews();

View File

@@ -28,21 +28,24 @@
<div class="row gy-3 align-items-stretch vehiclesContainer"> <div class="row gy-3 align-items-stretch vehiclesContainer">
@foreach (Vehicle vehicle in Model) @foreach (Vehicle vehicle in Model)
{ {
<div class="col-xl-2 col-lg-4 col-md-4 col-sm-4 col-4 user-select-none garage-item" ondragover="dragOver(event)" ondrop="dropBox(event, @vehicle.Id)" draggable="true" ondragstart="dragStart(event, @vehicle.Id)" data-tags='@string.Join(" ", vehicle.Tags)' id="gridVehicle_@vehicle.Id" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="@await Html.PartialAsync("_VehicleExtraFields", vehicle.ExtraFields)" data-bs-placement="bottom" data-bs-trigger="manual" onmouseenter="loadPinnedNotes(@vehicle.Id)" ontouchstart="loadPinnedNotes(@vehicle.Id)" ontouchcancel="hidePinnedNotes(@vehicle.Id)" ontouchend="hidePinnedNotes(@vehicle.Id)" onmouseleave="hidePinnedNotes(@vehicle.Id)"> @if (!(userConfig.HideSoldVehicles && !string.IsNullOrWhiteSpace(vehicle.SoldDate)))
<div class="card" onclick="viewVehicle(@vehicle.Id)"> {
<img src="@vehicle.ImageLocation" style="height:145px; object-fit:scale-down; pointer-events:none; @(string.IsNullOrWhiteSpace(vehicle.SoldDate) ? "" : "filter: grayscale(100%);")" /> <div class="col-xl-2 col-lg-4 col-md-4 col-sm-4 col-4 user-select-none garage-item" ondragover="dragOver(event)" ondrop="dropBox(event, @vehicle.Id)" draggable="true" ondragstart="dragStart(event, @vehicle.Id)" data-tags='@string.Join(" ", vehicle.Tags)' id="gridVehicle_@vehicle.Id" data-bs-toggle="tooltip" data-bs-html="true" data-bs-title="@await Html.PartialAsync("_VehicleExtraFields", vehicle.ExtraFields)" data-bs-placement="bottom" data-bs-trigger="manual" onmouseenter="loadPinnedNotes(@vehicle.Id)" ontouchstart="loadPinnedNotes(@vehicle.Id)" ontouchcancel="hidePinnedNotes(@vehicle.Id)" ontouchend="hidePinnedNotes(@vehicle.Id)" onmouseleave="hidePinnedNotes(@vehicle.Id)">
@if (!string.IsNullOrWhiteSpace(vehicle.SoldDate)) <div class="card" onclick="viewVehicle(@vehicle.Id)">
{ <img src="@vehicle.ImageLocation" style="height:145px; object-fit:scale-down; pointer-events:none; @(string.IsNullOrWhiteSpace(vehicle.SoldDate) ? "" : "filter: grayscale(100%);")" />
<div class="vehicle-sold-banner"><p class='display-6 mb-0'>@translator.Translate(userLanguage, "SOLD")</p></div> @if (!string.IsNullOrWhiteSpace(vehicle.SoldDate))
} {
<div class="card-body"> <div class="vehicle-sold-banner"><p class='display-6 mb-0'>@translator.Translate(userLanguage, "SOLD")</p></div>
<h5 class="card-title text-truncate garage-item-year" data-unit="@vehicle.Year">@($"{vehicle.Year}")</h5> }
<h5 class="card-title text-truncate">@($"{vehicle.Make}")</h5> <div class="card-body">
<h5 class="card-title text-truncate">@($"{vehicle.Model}")</h5> <h5 class="card-title text-truncate garage-item-year" data-unit="@vehicle.Year">@($"{vehicle.Year}")</h5>
<p class="card-text text-truncate">@vehicle.LicensePlate</p> <h5 class="card-title text-truncate">@($"{vehicle.Make}")</h5>
<h5 class="card-title text-truncate">@($"{vehicle.Model}")</h5>
<p class="card-text text-truncate">@vehicle.LicensePlate</p>
</div>
</div> </div>
</div> </div>
</div> }
} }
<div class="col-xl-2 col-lg-4 col-md-4 col-sm-4 col-4 garage-item-add"> <div class="col-xl-2 col-lg-4 col-md-4 col-sm-4 col-4 garage-item-add">
<div class="card" onclick="showAddVehicleModal()" style="height:100%;"> <div class="card" onclick="showAddVehicleModal()" style="height:100%;">

View File

@@ -57,6 +57,10 @@
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableExtraFieldColumns" checked="@Model.UserConfig.EnableExtraFieldColumns"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableExtraFieldColumns" checked="@Model.UserConfig.EnableExtraFieldColumns">
<label class="form-check-label" for="enableExtraFieldColumns">@translator.Translate(userLanguage, "Show Extra Field Columns")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Enabling this may cause performance issues")</small></label> <label class="form-check-label" for="enableExtraFieldColumns">@translator.Translate(userLanguage, "Show Extra Field Columns")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Enabling this may cause performance issues")</small></label>
</div> </div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="hideSoldVehicles" checked="@Model.UserConfig.HideSoldVehicles">
<label class="form-check-label" for="hideSoldVehicles">@translator.Translate(userLanguage, "Hide Sold Vehicles")</label>
</div>
<div class="form-check form-switch @(User.IsInRole(nameof(UserData.IsRootUser)) ? "" : "d-none")"> <div class="form-check form-switch @(User.IsInRole(nameof(UserData.IsRootUser)) ? "" : "d-none")">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableShopSupplies" checked="@Model.UserConfig.EnableShopSupplies"> <input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableShopSupplies" checked="@Model.UserConfig.EnableShopSupplies">
<label class="form-check-label" for="enableShopSupplies">@translator.Translate(userLanguage, "Shop Supplies")</label> <label class="form-check-label" for="enableShopSupplies">@translator.Translate(userLanguage, "Shop Supplies")</label>
@@ -184,10 +188,13 @@
</div> </div>
<div class="row"> <div class="row">
<div class="col-12 col-md-6"> <div class="col-12 col-md-6">
<span class="lead">@translator.Translate(userLanguage, "Manage Extra Fields")</span> <span class="lead">@translator.Translate(userLanguage, "Server-wide Settings")</span>
<div class="row"> <div class="row">
<div class="col-12 d-grid"> <div class="col-6 d-grid">
<button onclick="showExtraFieldModal()" class="btn btn-primary btn-md">@translator.Translate(userLanguage, "Add/Remove Extra Fields")</button> <button onclick="showExtraFieldModal()" class="btn btn-primary btn-md">@translator.Translate(userLanguage, "Extra Fields")</button>
</div>
<div class="col-6 d-grid">
<button onclick="showReminderUrgencyThresholdModal()" class="btn btn-primary btn-md">@translator.Translate(userLanguage, "Reminders")</button>
</div> </div>
</div> </div>
</div> </div>
@@ -205,7 +212,7 @@
<img src="/defaults/lubelogger_logo.png" /> <img src="/defaults/lubelogger_logo.png" />
</div> </div>
<div class="d-flex justify-content-center"> <div class="d-flex justify-content-center">
<small class="text-body-secondary">Version 1.2.4</small> <small class="text-body-secondary">@($"{translator.Translate(userLanguage, "Version")} {StaticHelper.VersionNumber}")</small>
</div> </div>
<p class="lead"> <p class="lead">
Proudly developed in the rural town of Price, Utah by Hargata Softworks. Proudly developed in the rural town of Price, Utah by Hargata Softworks.
@@ -250,6 +257,53 @@
</div> </div>
</div> </div>
<script> <script>
function showReminderUrgencyThresholdModal(){
Swal.fire({
title: decodeHTMLEntities('@translator.Translate(userLanguage, "Configure Reminder Urgency Thresholds")'),
html: `
<form>
<div class='d-flex flex-row align-items-center'>
<label class='form-label me-auto'>${decodeHTMLEntities('@translator.Translate(userLanguage, "Urgent(Days)")')}</label>
<input type="text" id="inputUrgentDays" class="form-control" style='width:40%' placeholder="${decodeHTMLEntities('@translator.Translate(userLanguage, "Urgent")')}" value='@Model.UserConfig.ReminderUrgencyConfig.UrgentDays'>
</div>
<div class='d-flex flex-row align-items-center mt-2'>
<label class='form-label me-auto'>${decodeHTMLEntities('@translator.Translate(userLanguage, "Very Urgent(Days)")')}</label>
<input type="text" id="inputVeryUrgentDays" class="form-control" style='width:40%' placeholder="${decodeHTMLEntities('@translator.Translate(userLanguage, "Very Urgent")')}" value='@Model.UserConfig.ReminderUrgencyConfig.VeryUrgentDays'>
</div>
<div class='d-flex flex-row align-items-center mt-2'>
<label class='form-label me-auto'>${decodeHTMLEntities('@translator.Translate(userLanguage, "Urgent(Distance)")')}</label>
<input type="text" id="inputUrgentDistance" class="form-control" style='width:40%' placeholder="${decodeHTMLEntities('@translator.Translate(userLanguage, "Urgent")')}" value='@Model.UserConfig.ReminderUrgencyConfig.UrgentDistance'>
</div>
<div class='d-flex flex-row align-items-center mt-2'>
<label class='form-label me-auto'>${decodeHTMLEntities('@translator.Translate(userLanguage, "Very Urgent(Distance)")')}</label>
<input type="text" id="inputVeryUrgentDistance" class="form-control" style='width:40%' placeholder="${decodeHTMLEntities('@translator.Translate(userLanguage, "Very Urgent")')}" value='@Model.UserConfig.ReminderUrgencyConfig.VeryUrgentDistance'>
</div>
</form>
`,
confirmButtonText: decodeHTMLEntities('@translator.Translate(userLanguage, "Save")'),
focusConfirm: false,
preConfirm: () => {
const urgentDays = $("#inputUrgentDays").val();
const veryUrgentDays = $("#inputVeryUrgentDays").val();
const urgentDistance = $("#inputUrgentDistance").val();
const veryUrgentDistance = $("#inputVeryUrgentDistance").val();
if (!urgentDays || isNaN(urgentDays) || !veryUrgentDays || isNaN(veryUrgentDays) || !urgentDistance || isNaN(urgentDistance) || !veryUrgentDistance || isNaN(veryUrgentDistance)) {
Swal.showValidationMessage(`Invalid parameters`)
}
return { urgentDays, veryUrgentDays, urgentDistance, veryUrgentDistance }
},
}).then(function (result) {
if (result.isConfirmed) {
$.post('/Home/SaveReminderUrgencyThreshold', { urgentDays: result.value.urgentDays, veryUrgentDays: result.value.veryUrgentDays, urgentDistance: result.value.urgentDistance, veryUrgentDistance: result.value.veryUrgentDistance }, function (data) {
if (data) {
setTimeout(function () { window.location.href = '/Home/Index?tab=settings' }, 500);
} else {
errorToast(genericErrorMessage());
}
})
}
});
}
function showExtraFieldModal() { function showExtraFieldModal() {
$.get(`/Home/GetExtraFieldsModal?importMode=0`, function (data) { $.get(`/Home/GetExtraFieldsModal?importMode=0`, function (data) {
$("#extraFieldModalContent").html(data); $("#extraFieldModalContent").html(data);
@@ -292,6 +346,7 @@
enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"), enableAutoOdometerInsert: $("#enableAutoOdometerInsert").is(":checked"),
enableShopSupplies: $("#enableShopSupplies").is(":checked"), enableShopSupplies: $("#enableShopSupplies").is(":checked"),
enableExtraFieldColumns: $("#enableExtraFieldColumns").is(":checked"), enableExtraFieldColumns: $("#enableExtraFieldColumns").is(":checked"),
hideSoldVehicles: $("#hideSoldVehicles").is(":checked"),
preferredGasUnit: $("#preferredGasUnit").val(), preferredGasUnit: $("#preferredGasUnit").val(),
preferredGasMileageUnit: $("#preferredFuelMileageUnit").val(), preferredGasMileageUnit: $("#preferredFuelMileageUnit").val(),
userLanguage: $("#defaultLanguage").val(), userLanguage: $("#defaultLanguage").val(),

View File

@@ -38,6 +38,11 @@
<button type="button" class="btn btn-secondary mt-2" onclick="remoteLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>@($"{translator.Translate(userLanguage, "Login via")} {openIdConfigName}")</button> <button type="button" class="btn btn-secondary mt-2" onclick="remoteLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>@($"{translator.Translate(userLanguage, "Login via")} {openIdConfigName}")</button>
</div> </div>
} }
<div class="d-flex justify-content-center">
<div class="form-group">
<small class="text-body-secondary">@config.GetMOTD()</small>
</div>
</div>
<div class="d-grid"> <div class="d-grid">
<a href="/Login/ForgotPassword" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Forgot Password")</a> <a href="/Login/ForgotPassword" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Forgot Password")</a>
</div> </div>

View File

@@ -64,7 +64,7 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="collisionRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label> <label for="collisionRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="collisionRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="collisionRecordFiles">
<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>
} }
@@ -80,7 +80,7 @@
</div> </div>
} }
<label for="collisionRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label> <label for="collisionRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="collisionRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="collisionRecordFiles">
<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>

View File

@@ -12,6 +12,7 @@
{ {
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.RepairRecord);
} }
@model List<CollisionRecord> @model List<CollisionRecord>
<div class="row"> <div class="row">
@@ -48,31 +49,31 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='date' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Date" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'RepairRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label> <label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='odometer' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Odometer" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'RepairRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label> <label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='description' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Description" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'RepairRecord')" type="checkbox" id="chkCol_Description" checked>
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label> <label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='cost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Cost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'RepairRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label> <label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'RepairRecord')" type="checkbox" id="chkCol_Notes" checked>
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -81,7 +82,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'RepairRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -106,14 +107,14 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th> <th scope="col" class="col-2 col-xl-1 flex-grow-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th> <th scope="col" class="col-2 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4" data-column="description">@translator.Translate(userLanguage, "Description")</th> <th scope="col" class="col-3 col-xl-4 flex-grow-1" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" data-column="cost" onclick="toggleSort('accident-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th> <th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('accident-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3" data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -121,14 +122,14 @@
@foreach (CollisionRecord collisionRecord in Model) @foreach (CollisionRecord collisionRecord in Model)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@collisionRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditCollisionRecordModal,@collisionRecord.Id)" data-tags='@string.Join(" ", collisionRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@collisionRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditCollisionRecordModal,@collisionRecord.Id)" data-tags='@string.Join(" ", collisionRecord.Tags)'>
<td class="col-2 col-xl-1" data-column="date">@collisionRecord.Date.ToShortDateString()</td> <td class="col-2 col-xl-1 flex-grow-1" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(collisionRecord.Date)">@collisionRecord.Date.ToShortDateString()</td>
<td class="col-2" data-column="odometer">@collisionRecord.Mileage</td> <td class="col-2 flex-grow-1" data-column="odometer">@collisionRecord.Mileage</td>
<td class="col-3 col-xl-4" data-column="description">@collisionRecord.Description</td> <td class="col-3 col-xl-4 flex-grow-1" data-column="description">@collisionRecord.Description</td>
<td class="col-2" data-column="cost" data-record-type="cost">@((hideZero && collisionRecord.Cost == default) ? "---" : collisionRecord.Cost.ToString("C"))</td> <td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && collisionRecord.Cost == default) ? "---" : collisionRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(collisionRecord.Notes)</td> <td class="col-3 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(collisionRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(collisionRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(collisionRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -156,4 +157,10 @@
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'RepairRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'RepairRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'RepairRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'RepairRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
</ul> <li><hr class="context-menu-active-multiple dropdown-divider"></li>
<li><a class="context-menu-active-multiple dropdown-item" href="#" onclick="getRecordsDeltaStats(selectedRow)">@translator.Translate(userLanguage, "Statistics")</a></li>
</ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@@ -46,6 +46,7 @@
{ {
extraFields = Model.GasRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.GasRecords.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.GasRecord);
} }
<div class="row"> <div class="row">
<div class="d-flex justify-content-between"> <div class="d-flex justify-content-between">
@@ -95,49 +96,49 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='daterefueled' onChange="showTableColumns(this)" type="checkbox" id="chkCol_DateRefueled" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='daterefueled' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_DateRefueled" checked>
<label class="form-check-label stretched-link" for="chkCol_DateRefueled">@translator.Translate(userLanguage, "Date Refueled")</label> <label class="form-check-label stretched-link" for="chkCol_DateRefueled">@translator.Translate(userLanguage, "Date Refueled")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='odometer' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Odometer" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label> <label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='delta' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Delta" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='delta' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_Delta" checked>
<label class="form-check-label stretched-link" for="chkCol_Delta">@translator.Translate(userLanguage, "Delta")</label> <label class="form-check-label stretched-link" for="chkCol_Delta">@translator.Translate(userLanguage, "Delta")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='consumption' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Consumption" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='consumption' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_Consumption" checked>
<label class="form-check-label stretched-link" for="chkCol_Consumption">@translator.Translate(userLanguage, "Consumption")</label> <label class="form-check-label stretched-link" for="chkCol_Consumption">@translator.Translate(userLanguage, "Consumption")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='fueleconomy' onChange="showTableColumns(this)" type="checkbox" id="chkCol_FuelEconomy" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='fueleconomy' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_FuelEconomy" checked>
<label class="form-check-label stretched-link" for="chkCol_FuelEconomy">@translator.Translate(userLanguage, "Fuel Economy")</label> <label class="form-check-label stretched-link" for="chkCol_FuelEconomy">@translator.Translate(userLanguage, "Fuel Economy")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='cost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Cost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label> <label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='unitcost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_UnitCost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='unitcost' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_UnitCost" checked>
<label class="form-check-label stretched-link" for="chkCol_UnitCost">@translator.Translate(userLanguage, "Unit Cost")</label> <label class="form-check-label stretched-link" for="chkCol_UnitCost">@translator.Translate(userLanguage, "Unit Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes"> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="chkCol_Notes">
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -146,7 +147,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'GasRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -170,17 +171,17 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2" data-column="daterefueled">@translator.Translate(userLanguage, "Date Refueled")</th> <th scope="col" class="col-2 flex-grow-1" data-column="daterefueled">@translator.Translate(userLanguage, "Date Refueled")</th>
<th scope="col" class="col-2" data-column="odometer">@($"{translator.Translate(userLanguage, "Odometer")}({distanceUnit})")</th> <th scope="col" class="col-2 flex-grow-1" data-column="odometer">@($"{translator.Translate(userLanguage, "Odometer")}({distanceUnit})")</th>
<th scope="col" class="col-1" data-column="delta" style="cursor:pointer;" onclick="toggleSort('gas-tab-pane', this)">@($"Δ({distanceUnit})")</th> <th scope="col" class="col-1 flex-grow-1" data-column="delta" style="cursor:pointer;" onclick="toggleSort('gas-tab-pane', this)">@($"Δ({distanceUnit})")</th>
<th scope="col" class="col-2" data-column="consumption" 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-2 flex-grow-1" data-column="consumption" 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-3" data-column="fueleconomy" 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 flex-grow-1" data-column="fueleconomy" 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" data-column="cost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th> <th scope="col" class="col-1 flex-grow-1" data-column="cost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-1" data-column="unitcost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Unit Cost")</th> <th scope="col" class="col-1 flex-grow-1" data-column="unitcost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Unit Cost")</th>
<th scope="col" class="col-3" style='display:none;' data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-3 flex-grow-1" style='display:none;' data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -188,17 +189,17 @@
@foreach (GasRecordViewModel gasRecord in Model.GasRecords) @foreach (GasRecordViewModel gasRecord in Model.GasRecords)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@gasRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditGasRecordModal,@gasRecord.Id)" data-tags='@string.Join(" ", gasRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@gasRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditGasRecordModal,@gasRecord.Id)" data-tags='@string.Join(" ", gasRecord.Tags)'>
<td class="col-2" data-column="daterefueled">@gasRecord.Date</td> <td class="col-2 flex-grow-1" data-column="daterefueled">@gasRecord.Date</td>
<td class="col-2" data-column="odometer" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage" data-gas-original="@gasRecord.Mileage">@gasRecord.Mileage</td> <td class="col-2 flex-grow-1" data-column="odometer" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage" data-gas-original="@gasRecord.Mileage">@gasRecord.Mileage</td>
<td class="col-1" data-column="delta">@(gasRecord.DeltaMileage == default ? "---" : gasRecord.DeltaMileage)</td> <td class="col-1 flex-grow-1" data-column="delta">@(gasRecord.DeltaMileage == default ? "---" : gasRecord.DeltaMileage)</td>
<td class="col-2" data-column="consumption" data-gas-type="consumption" data-gas-aggregate="@gasRecord.Gallons">@gasRecord.Gallons.ToString("F")</td> <td class="col-2 flex-grow-1" data-column="consumption" data-gas-type="consumption" data-gas-aggregate="@gasRecord.Gallons">@gasRecord.Gallons.ToString("F")</td>
<td class="col-3" data-column="fueleconomy" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td> <td class="col-3 flex-grow-1" data-column="fueleconomy" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-1" data-column="cost" data-record-type="cost">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td> <td class="col-1 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td>
<td class="col-1" data-column="unitcost" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td> <td class="col-1 flex-grow-1" data-column="unitcost" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td>
<td class="col-3 text-truncate" style='display:none;' data-column="notes">@StaticHelper.TruncateStrings(gasRecord.Notes)</td> <td class="col-3 flex-grow-1 text-truncate" style='display:none;' data-column="notes">@StaticHelper.TruncateStrings(gasRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(gasRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(gasRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -222,7 +223,10 @@
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'GasRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'GasRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'GasRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'GasRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
</ul> </ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}
<script> <script>
@if (!string.IsNullOrWhiteSpace(preferredFuelEconomyUnit)) @if (!string.IsNullOrWhiteSpace(preferredFuelEconomyUnit))
{ {

View File

@@ -103,14 +103,14 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.GasRecord.Files) @await Html.PartialAsync("_UploadedFiles", Model.GasRecord.Files)
<label for="gasRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label> <label for="gasRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="gasRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="gasRecordFiles">
<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="gasRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label> <label for="gasRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="gasRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="gasRecordFiles">
<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>

View File

@@ -34,14 +34,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="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"> <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="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"> <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>
} }

View File

@@ -48,14 +48,14 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="odometerRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label> <label for="odometerRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="odometerRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="odometerRecordFiles">
<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="odometerRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label> <label for="odometerRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="odometerRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="odometerRecordFiles">
<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>

View File

@@ -12,6 +12,7 @@
{ {
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x=>x.Tab == ImportMode.OdometerRecord);
} }
@model List<OdometerRecord> @model List<OdometerRecord>
<div class="row"> <div class="row">
@@ -47,19 +48,19 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='date' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Date" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label> <label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='odometer' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Odometer" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label> <label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_Notes" checked>
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -68,7 +69,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -93,12 +94,12 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th> <th scope="col" class="col-2 col-xl-1 flex-grow-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-3" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th> <th scope="col" class="col-3 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-7 col-xl-8" data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-7 col-xl-8 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -106,12 +107,12 @@
@foreach (OdometerRecord odometerRecord in Model) @foreach (OdometerRecord odometerRecord in Model)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@odometerRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditOdometerRecordModal,@odometerRecord.Id)" data-tags='@string.Join(" ", odometerRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@odometerRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditOdometerRecordModal,@odometerRecord.Id)" data-tags='@string.Join(" ", odometerRecord.Tags)'>
<td class="col-2 col-xl-1" data-column="date">@odometerRecord.Date.ToShortDateString()</td> <td class="col-2 col-xl-1 flex-grow-1" data-column="date">@odometerRecord.Date.ToShortDateString()</td>
<td class="col-3" data-column="odometer" data-record-type="cost">@odometerRecord.Mileage</td> <td class="col-3 flex-grow-1" data-column="odometer" data-record-type="cost">@odometerRecord.Mileage</td>
<td class="col-7 col-xl-8 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td> <td class="col-7 col-xl-8 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(odometerRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(odometerRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -134,4 +135,8 @@
<li><hr class="context-menu-multiple dropdown-divider"></li> <li><hr class="context-menu-multiple dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'OdometerRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'OdometerRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'OdometerRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'OdometerRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
</ul> </ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@@ -65,14 +65,14 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="planRecordFiles">@translator.Translate(userLanguage, "Upload more documents")</label> <label for="planRecordFiles">@translator.Translate(userLanguage, "Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="planRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="planRecordFiles">
<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="planRecordFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label> <label for="planRecordFiles">@translator.Translate(userLanguage, "Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="planRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="planRecordFiles">
<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>

View File

@@ -64,7 +64,7 @@
<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="serviceRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="serviceRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="serviceRecordFiles">
<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>
} }
@@ -80,7 +80,7 @@
</div> </div>
} }
<label for="serviceRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label> <label for="serviceRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="serviceRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="serviceRecordFiles">
<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>

View File

@@ -12,6 +12,7 @@
{ {
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.ServiceRecord);
} }
@model List<ServiceRecord> @model List<ServiceRecord>
<div class="row"> <div class="row">
@@ -48,31 +49,31 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='date' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Date" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'ServiceRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label> <label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='odometer' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Odometer" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'ServiceRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label> <label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='description' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Description" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'ServiceRecord')" type="checkbox" id="chkCol_Description" checked>
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label> <label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='cost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Cost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'ServiceRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label> <label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'ServiceRecord')" type="checkbox" id="chkCol_Notes" checked>
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -81,7 +82,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'ServiceRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -106,14 +107,14 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th> <th scope="col" class="col-2 col-xl-1 flex-grow-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th> <th scope="col" class="col-2 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4" data-column="description">@translator.Translate(userLanguage, "Description")</th> <th scope="col" class="col-3 col-xl-4 flex-grow-1" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" data-column="cost" onclick="toggleSort('servicerecord-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th> <th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('servicerecord-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3" data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -121,14 +122,14 @@
@foreach (ServiceRecord serviceRecord in Model) @foreach (ServiceRecord serviceRecord in Model)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@serviceRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditServiceRecordModal,@serviceRecord.Id)" data-tags='@string.Join(" ", serviceRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@serviceRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditServiceRecordModal,@serviceRecord.Id)" data-tags='@string.Join(" ", serviceRecord.Tags)'>
<td class="col-2 col-xl-1" data-column="date">@serviceRecord.Date.ToShortDateString()</td> <td class="col-2 col-xl-1 flex-grow-1" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(serviceRecord.Date)">@serviceRecord.Date.ToShortDateString()</td>
<td class="col-2" data-column="odometer">@serviceRecord.Mileage</td> <td class="col-2 flex-grow-1" data-column="odometer">@serviceRecord.Mileage</td>
<td class="col-3 col-xl-4" data-column="description">@serviceRecord.Description</td> <td class="col-3 col-xl-4 flex-grow-1" data-column="description">@serviceRecord.Description</td>
<td class="col-2" data-column="cost" data-record-type="cost">@((hideZero && serviceRecord.Cost == default) ? "---" : serviceRecord.Cost.ToString("C"))</td> <td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && serviceRecord.Cost == default) ? "---" : serviceRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(serviceRecord.Notes)</td> <td class="col-3 text-truncate flex-grow-1" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(serviceRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(serviceRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(serviceRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -156,4 +157,10 @@
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'ServiceRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'ServiceRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'ServiceRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'ServiceRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
<li><hr class="context-menu-active-multiple dropdown-divider"></li>
<li><a class="context-menu-active-multiple dropdown-item" href="#" onclick="getRecordsDeltaStats(selectedRow)">@translator.Translate(userLanguage, "Statistics")</a></li>
</ul> </ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@@ -67,14 +67,14 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="supplyRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label> <label for="supplyRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="supplyRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="supplyRecordFiles">
<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="supplyRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label> <label for="supplyRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="supplyRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="supplyRecordFiles">
<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>

View File

@@ -12,6 +12,7 @@
{ {
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.SupplyRecord);
} }
@model List<SupplyRecord> @model List<SupplyRecord>
<div class="row"> <div class="row">
@@ -48,43 +49,43 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='date' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Date" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label> <label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='partnumber' onChange="showTableColumns(this)" type="checkbox" id="chkCol_PartNumber" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='partnumber' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_PartNumber" checked>
<label class="form-check-label stretched-link" for="chkCol_PartNumber">@translator.Translate(userLanguage, "Part Number")</label> <label class="form-check-label stretched-link" for="chkCol_PartNumber">@translator.Translate(userLanguage, "Part Number")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='supplier' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Supplier" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='supplier' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Supplier" checked>
<label class="form-check-label stretched-link" for="chkCol_Supplier">@translator.Translate(userLanguage, "Supplier")</label> <label class="form-check-label stretched-link" for="chkCol_Supplier">@translator.Translate(userLanguage, "Supplier")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='description' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Description" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Description" checked>
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label> <label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='quantity' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Quantity" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='quantity' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Quantity" checked>
<label class="form-check-label stretched-link" for="chkCol_Quantity">@translator.Translate(userLanguage, "Quantity")</label> <label class="form-check-label stretched-link" for="chkCol_Quantity">@translator.Translate(userLanguage, "Quantity")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='cost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Cost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label> <label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="chkCol_Notes" checked>
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -93,7 +94,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'SupplyRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -118,16 +119,16 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th> <th scope="col" class="col-2 flex-grow-1 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2" data-column="partnumber">@translator.Translate(userLanguage, "Part #")</th> <th scope="col" class="col-2 flex-grow-1" data-column="partnumber">@translator.Translate(userLanguage, "Part #")</th>
<th scope="col" class="col-2" data-column="supplier">@translator.Translate(userLanguage, "Supplier")</th> <th scope="col" class="col-2 flex-grow-1" data-column="supplier">@translator.Translate(userLanguage, "Supplier")</th>
<th scope="col" class="col-2 col-xl-3" data-column="description">@translator.Translate(userLanguage, "Description")</th> <th scope="col" class="col-2 flex-grow-1 col-xl-3" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-1" data-column="quantity" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Quantity")</th> <th scope="col" class="col-1 flex-grow-1" data-column="quantity" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Quantity")</th>
<th scope="col" class="col-1" data-column="cost" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th> <th scope="col" class="col-1 flex-grow-1" data-column="cost" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-2" data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-2 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -135,16 +136,16 @@
@foreach (SupplyRecord supplyRecord in Model) @foreach (SupplyRecord supplyRecord in Model)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@supplyRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditSupplyRecordModal,@supplyRecord.Id)" data-tags='@string.Join(" ", supplyRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@supplyRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditSupplyRecordModal,@supplyRecord.Id)" data-tags='@string.Join(" ", supplyRecord.Tags)'>
<td class="col-2 col-xl-1" data-column="date">@supplyRecord.Date.ToShortDateString()</td> <td class="col-2 flex-grow-1 col-xl-1" data-column="date">@supplyRecord.Date.ToShortDateString()</td>
<td class="col-2" data-column="partnumber">@supplyRecord.PartNumber</td> <td class="col-2 flex-grow-1" data-column="partnumber">@supplyRecord.PartNumber</td>
<td class="col-2" data-column="supplier">@supplyRecord.PartSupplier</td> <td class="col-2 flex-grow-1" data-column="supplier">@supplyRecord.PartSupplier</td>
<td class="col-2 col-xl-3" data-column="description">@supplyRecord.Description</td> <td class="col-2 flex-grow-1 col-xl-3" data-column="description">@supplyRecord.Description</td>
<td class="col-1" data-column="quantity">@supplyRecord.Quantity</td> <td class="col-1 flex-grow-1" data-column="quantity">@supplyRecord.Quantity</td>
<td class="col-1" data-column="cost" data-record-type="cost">@((hideZero && supplyRecord.Cost == default) ? "---" : supplyRecord.Cost.ToString("C"))</td> <td class="col-1 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && supplyRecord.Cost == default) ? "---" : supplyRecord.Cost.ToString("C"))</td>
<td class="col-2 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(supplyRecord.Notes)</td> <td class="col-2 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(supplyRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(supplyRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(supplyRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -167,4 +168,8 @@
<li><hr class="context-menu-multiple dropdown-divider"></li> <li><hr class="context-menu-multiple dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'SupplyRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'SupplyRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'SupplyRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'SupplyRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
</ul> </ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@@ -73,7 +73,7 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="taxRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label> <label for="taxRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="taxRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="taxRecordFiles">
<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>
} }
@@ -89,7 +89,7 @@
</div> </div>
} }
<label for="taxRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label> <label for="taxRecordFiles">@translator.Translate(userLanguage,"Upload documents(optional)")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="taxRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="taxRecordFiles">
<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>

View File

@@ -12,6 +12,7 @@
{ {
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.TaxRecord);
} }
@model List<TaxRecord> @model List<TaxRecord>
<div class="row"> <div class="row">
@@ -48,25 +49,25 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='date' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Date" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'TaxRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label> <label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='description' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Description" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'TaxRecord')" type="checkbox" id="chkCol_Description" checked>
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label> <label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='cost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Cost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'TaxRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label> <label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'TaxRecord')" type="checkbox" id="chkCol_Notes" checked>
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -75,7 +76,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'TaxRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -100,13 +101,13 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-3 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th> <th scope="col" class="col-3 flex-grow-1 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-4 col-xl-6" data-column="description">@translator.Translate(userLanguage, "Description")</th> <th scope="col" class="col-4 flex-grow-1 col-xl-6" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" data-column="cost" onclick="toggleSort('tax-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th> <th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('tax-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3" data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -114,13 +115,13 @@
@foreach (TaxRecord taxRecord in Model) @foreach (TaxRecord taxRecord in Model)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@taxRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditTaxRecordModal,@taxRecord.Id)" data-tags='@string.Join(" ", taxRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@taxRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditTaxRecordModal,@taxRecord.Id)" data-tags='@string.Join(" ", taxRecord.Tags)'>
<td class="col-3 col-xl-1" data-column="date">@taxRecord.Date.ToShortDateString()</td> <td class="col-3 flex-grow-1 col-xl-1" data-column="date">@taxRecord.Date.ToShortDateString()</td>
<td class="col-4 col-xl-6" data-column="description">@taxRecord.Description</td> <td class="col-4 flex-grow-1 col-xl-6" data-column="description">@taxRecord.Description</td>
<td class="col-2" data-column="cost" data-record-type="cost">@((hideZero && taxRecord.Cost == default) ? "---" : taxRecord.Cost.ToString("C"))</td> <td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && taxRecord.Cost == default) ? "---" : taxRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(taxRecord.Notes)</td> <td class="col-3 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(taxRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(taxRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(taxRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -143,4 +144,8 @@
<li><hr class="context-menu-multiple dropdown-divider"></li> <li><hr class="context-menu-multiple dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'TaxRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'TaxRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'TaxRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'TaxRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
</ul> </ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@@ -64,7 +64,7 @@
<div> <div>
@await Html.PartialAsync("_UploadedFiles", Model.Files) @await Html.PartialAsync("_UploadedFiles", Model.Files)
<label for="upgradeRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label> <label for="upgradeRecordFiles">@translator.Translate(userLanguage,"Upload more documents")</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="upgradeRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="upgradeRecordFiles">
<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>
} }
@@ -80,7 +80,7 @@
</div> </div>
} }
<label for="upgradeRecordFiles">Upload documents(optional)</label> <label for="upgradeRecordFiles">Upload documents(optional)</label>
<input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="upgradeRecordFiles"> <input onChange="uploadVehicleFilesAsync(this)" type="file" multiple accept="@config.GetAllowedFileUploadExtensions()" class="form-control-file" id="upgradeRecordFiles">
<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>

View File

@@ -12,6 +12,7 @@
{ {
extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList(); extraFields = Model.SelectMany(x => x.ExtraFields).Select(y => y.Name).Distinct().ToList();
} }
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.UpgradeRecord);
} }
@model List<UpgradeRecord> @model List<UpgradeRecord>
<div class="row"> <div class="row">
@@ -48,31 +49,31 @@
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li> <li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='date' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Date" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'UpgradeRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label> <label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='odometer' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Odometer" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'UpgradeRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label> <label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='description' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Description" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'UpgradeRecord')" type="checkbox" id="chkCol_Description" checked>
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label> <label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='cost' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Cost" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'UpgradeRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label> <label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div> </div>
</li> </li>
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" data-column-toggle='notes' onChange="showTableColumns(this)" type="checkbox" id="chkCol_Notes" checked> <input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'UpgradeRecord')" type="checkbox" id="chkCol_Notes" checked>
<label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label> <label class="form-check-label stretched-link" for="chkCol_Notes">@translator.Translate(userLanguage, "Notes")</label>
</div> </div>
</li> </li>
@@ -81,7 +82,7 @@
var elementId = Guid.NewGuid(); var elementId = Guid.NewGuid();
<li class="dropdown-item"> <li class="dropdown-item">
<div class="list-group-item"> <div class="list-group-item">
<input class="form-check-input" onChange="showTableColumns(this, true)" type="checkbox" id="@elementId"> <input class="form-check-input col-visible-toggle" data-column-toggle='@extraFieldColumn' onChange="showTableColumns(this, 'UpgradeRecord')" type="checkbox" id="@elementId">
<label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label> <label class="form-check-label stretched-link" for="@elementId">@extraFieldColumn</label>
</div> </div>
</li> </li>
@@ -106,14 +107,14 @@
<table class="table table-hover"> <table class="table table-hover">
<thead class="sticky-top"> <thead class="sticky-top">
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-2 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th> <th scope="col" class="col-2 flex-grow-1 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th> <th scope="col" class="col-2 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4" data-column="description">@translator.Translate(userLanguage, "Description")</th> <th scope="col" class="col-3 flex-grow-1 col-xl-4" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2" data-column="cost" onclick="toggleSort('upgrade-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th> <th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('upgrade-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3" data-column="notes">@translator.Translate(userLanguage, "Notes")</th> <th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<th scope="col" style='display:none;' class="col-2" data-column="@extraFieldColumn">@extraFieldColumn</th> <th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
} }
</tr> </tr>
</thead> </thead>
@@ -121,14 +122,14 @@
@foreach (UpgradeRecord upgradeRecord in Model) @foreach (UpgradeRecord upgradeRecord in Model)
{ {
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@upgradeRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditUpgradeRecordModal,@upgradeRecord.Id)" data-tags='@string.Join(" ", upgradeRecord.Tags)'> <tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@upgradeRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditUpgradeRecordModal,@upgradeRecord.Id)" data-tags='@string.Join(" ", upgradeRecord.Tags)'>
<td class="col-2 col-xl-1" data-column="date">@upgradeRecord.Date.ToShortDateString()</td> <td class="col-2 flex-grow-1 col-xl-1" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(upgradeRecord.Date)">@upgradeRecord.Date.ToShortDateString()</td>
<td class="col-2" data-column="odometer">@upgradeRecord.Mileage</td> <td class="col-2 flex-grow-1" data-column="odometer">@upgradeRecord.Mileage</td>
<td class="col-3 col-xl-4" data-column="description">@upgradeRecord.Description</td> <td class="col-3 flex-grow-1 col-xl-4" data-column="description">@upgradeRecord.Description</td>
<td class="col-2" data-column="cost" data-record-type="cost">@((hideZero && upgradeRecord.Cost == default) ? "---" : upgradeRecord.Cost.ToString("C"))</td> <td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && upgradeRecord.Cost == default) ? "---" : upgradeRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(upgradeRecord.Notes)</td> <td class="col-3 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(upgradeRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields) @foreach (string extraFieldColumn in extraFields)
{ {
<td class="col-2 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(upgradeRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td> <td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(upgradeRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
} }
</tr> </tr>
} }
@@ -155,4 +156,10 @@
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'UpgradeRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li> <li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'UpgradeRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'UpgradeRecord')">@translator.Translate(userLanguage, "Delete")</a></li> <li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'UpgradeRecord')">@translator.Translate(userLanguage, "Delete")</a></li>
</ul> <li><hr class="context-menu-active-multiple dropdown-divider"></li>
<li><a class="context-menu-active-multiple dropdown-item" href="#" onclick="getRecordsDeltaStats(selectedRow)">@translator.Translate(userLanguage, "Statistics")</a></li>
</ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@@ -0,0 +1,9 @@
@model IEnumerable<UserColumnPreference>
<script>
var visibleColumns = [];
@foreach(string visibleColumn in Model.SelectMany(x=> x.VisibleColumns))
{
@:visibleColumns.push(decodeHTMLEntities('@visibleColumn'));
}
loadUserColumnPreferences(visibleColumns);
</script>

View File

@@ -19,7 +19,9 @@
"UseUKMPG": false, "UseUKMPG": false,
"UseThreeDecimalGasCost": true, "UseThreeDecimalGasCost": true,
"UseMarkDownOnSavedNotes": false, "UseMarkDownOnSavedNotes": false,
"HideSoldVehicles": false,
"PreferredGasMileageUnit": "", "PreferredGasMileageUnit": "",
"UserColumnPreferences": [],
"PreferredGasUnit": "", "PreferredGasUnit": "",
"UserLanguage": "en_US", "UserLanguage": "en_US",
"VisibleTabs": [ 0, 1, 4, 2, 3, 6, 5, 8 ], "VisibleTabs": [ 0, 1, 4, 2, 3, 6, 5, 8 ],

View File

@@ -0,0 +1,45 @@
---
version: "3.4"
services:
app:
image: ghcr.io/hargata/lubelogger:latest
build: .
restart: unless-stopped
# volumes used to keep data persistent
volumes:
- config:/App/config
- data:/App/data
- translations:/App/wwwroot/translations
- documents:/App/wwwroot/documents
- images:/App/wwwroot/images
- temp:/App/wwwroot/temp
- log:/App/log
- keys:/root/.aspnet/DataProtection-Keys
# expose port and/or use serving via traefik
ports:
- 8080:8080
env_file:
- .env
postgres:
image: postgres:14
restart: unless-stopped
environment:
POSTGRES_USER: "lubelogger"
POSTGRES_PASSWORD: "lubepass"
POSTGRES_DB: "lubelogger"
volumes:
- postgres:/var/lib/postgresql/data
- /etc/localtime:/etc/localtime:ro
volumes:
config:
data:
translations:
documents:
images:
temp:
log:
keys:
postgres:

File diff suppressed because one or more lines are too long

View File

@@ -403,6 +403,16 @@ function searchGasTableRows() {
if (result.isConfirmed) { if (result.isConfirmed) {
var rowData = $(`#${tabName} table tbody tr`); var rowData = $(`#${tabName} table tbody tr`);
var filteredRows = $(`#${tabName} table tbody tr td:contains('${result.value.searchString}')`).parent(); var filteredRows = $(`#${tabName} table tbody tr td:contains('${result.value.searchString}')`).parent();
var splitSearchString = result.value.searchString.split('=');
if (result.value.searchString.includes('=') && splitSearchString.length == 2) {
//column specific search.
//get column index
var columns = $(`#${tabName} table th`).toArray().map(x => x.innerText);
var columnName = splitSearchString[0];
var colSearchString = splitSearchString[1];
var colIndex = columns.findIndex(x => x == columnName) + 1;
filteredRows = $(`#${tabName} table tbody tr td:nth-child(${colIndex}):contains('${colSearchString}')`).parent();
}
if (result.value.searchString.trim() == '') { if (result.value.searchString.trim() == '') {
rowData.removeClass('override-hide'); rowData.removeClass('override-hide');
} else { } else {

View File

@@ -678,6 +678,9 @@ function isRightClick(e) {
return false; return false;
} }
function stopEvent() { function stopEvent() {
if (isDragging) {
isDragging = false;
}
event.stopPropagation(); event.stopPropagation();
} }
function rangeMouseUp(e) { function rangeMouseUp(e) {
@@ -847,7 +850,7 @@ function replenishSupplies() {
var quantitybeforeRepl = globalParseFloat($('#supplyRecordQuantity').val()); var quantitybeforeRepl = globalParseFloat($('#supplyRecordQuantity').val());
if (isNaN(replquantity) || (replcost.trim() != '' && isNaN(parsedReplCost))) { if (isNaN(replquantity) || (replcost.trim() != '' && isNaN(parsedReplCost))) {
Swal.showValidationMessage(`Please enter a valid quantity and cost`); Swal.showValidationMessage(`Please enter a valid quantity and cost`);
} else if (replcost.trim() == '' && (isNaN(quantitybeforeRepl) || quantitybeforeRepl == 0)){ } else if (replcost.trim() == '' && (isNaN(quantitybeforeRepl) || quantitybeforeRepl == 0)) {
Swal.showValidationMessage(`Unable to use unit cost calculation, please provide cost`); Swal.showValidationMessage(`Unable to use unit cost calculation, please provide cost`);
} }
return { replquantity, replcost, parsedReplCost } return { replquantity, replcost, parsedReplCost }
@@ -879,25 +882,16 @@ function replenishSupplies() {
} }
}); });
} }
function showTableColumns(e, isExtraField) { function showTableColumns(e, tabName) {
//logic for extra field since we dont hardcode the data-column type //logic for extra field since we dont hardcode the data-column type
if (isExtraField) { var showColumn = $(e).is(':checked');
var showColumn = $(e).is(':checked'); var columnName = $(e).attr('data-column-toggle');
var columnName = $(e).parent().find('.form-check-label').text(); if (showColumn) {
if (showColumn) { $(`[data-column='${columnName}']`).show();
$(`[data-column='${columnName}']`).show();
} else {
$(`[data-column='${columnName}']`).hide();
}
} else { } else {
var showColumn = $(e).is(':checked'); $(`[data-column='${columnName}']`).hide();
var columnName = $(e).attr('data-column-toggle');
if (showColumn) {
$(`[data-column='${columnName}']`).show();
} else {
$(`[data-column='${columnName}']`).hide();
}
} }
saveUserColumnPreferences(tabName);
} }
function searchTableRows(tabName) { function searchTableRows(tabName) {
Swal.fire({ Swal.fire({
@@ -915,6 +909,16 @@ function searchTableRows(tabName) {
if (result.isConfirmed) { if (result.isConfirmed) {
var rowData = $(`#${tabName} table tbody tr`); var rowData = $(`#${tabName} table tbody tr`);
var filteredRows = $(`#${tabName} table tbody tr td:contains('${result.value.searchString}')`).parent(); var filteredRows = $(`#${tabName} table tbody tr td:contains('${result.value.searchString}')`).parent();
var splitSearchString = result.value.searchString.split('=');
if (result.value.searchString.includes('=') && splitSearchString.length == 2) {
//column specific search.
//get column index
var columns = $(`#${tabName} table th`).toArray().map(x => x.innerText);
var columnName = splitSearchString[0];
var colSearchString = splitSearchString[1];
var colIndex = columns.findIndex(x => x == columnName) + 1;
filteredRows = $(`#${tabName} table tbody tr td:nth-child(${colIndex}):contains('${colSearchString}')`).parent();
}
if (result.value.searchString.trim() == '') { if (result.value.searchString.trim() == '') {
rowData.removeClass('override-hide'); rowData.removeClass('override-hide');
} else { } else {
@@ -925,4 +929,34 @@ function searchTableRows(tabName) {
updateAggregateLabels(); updateAggregateLabels();
} }
}); });
}
function loadUserColumnPreferences(columns) {
if (columns.length == 0) {
//user has no preference saved, reset to default
return;
}
//uncheck all columns
$(".col-visible-toggle").prop("checked", false);
//hide all columns
$('[data-column]').hide();
//toggle visibility of each column
columns.map(x => {
var defaultColumn = $(`[data-column-toggle='${x}'].col-visible-toggle`);
if (defaultColumn.length > 0) {
defaultColumn.prop("checked", true);
$(`[data-column='${x}']`).show();
}
});
}
function saveUserColumnPreferences(importMode) {
var visibleColumns = $('.col-visible-toggle:checked').map((index, elem) => $(elem).attr('data-column-toggle')).toArray();
var columnPreference = {
tab: importMode,
visibleColumns: visibleColumns
};
$.post('/Vehicle/SaveUserColumnPreferences', { columnPreference: columnPreference }, function (data) {
if (!data) {
errorToast(genericErrorMessage());
}
});
} }

View File

@@ -440,4 +440,54 @@ function getAndValidateGenericRecordValues() {
tags: genericTags tags: genericTags
} }
} }
}
function getRecordsDeltaStats(recordIds) {
if (recordIds.length < 2) {
return;
}
var odometerReadings = [];
var dateReadings = [];
var costReadings = [];
//get all of the odometer readings
recordIds.map(x => {
var odometerReading = parseInt($(`tr[data-rowId='${x}'] td[data-column='odometer']`).text());
if (!isNaN(odometerReading)) {
odometerReadings.push(odometerReading);
}
var dateReading = parseInt($(`tr[data-rowId=${x}] td[data-column='date']`).attr('data-date'));
if (!isNaN(dateReading)) {
dateReadings.push(dateReading);
}
var costReading = globalParseFloat($(`tr[data-rowId='${x}'] td[data-column='cost']`).text());
if (costReading > 0) {
costReadings.push(costReading);
}
});
//get max stats
var maxOdo = odometerReadings.length > 0 ? odometerReadings.reduce((a, b) => a > b ? a : b) : 0;
var maxDate = dateReadings.length > 0 ? dateReadings.reduce((a, b) => a > b ? a : b) : 0;
//get min stats
var minOdo = odometerReadings.length > 0 ? odometerReadings.reduce((a, b) => a < b ? a : b) : 0;
var minDate = dateReadings.length > 0 ? dateReadings.reduce((a, b) => a < b ? a : b) : 0;
//get sum of costs
var costSum = costReadings.length > 0 ? costReadings.reduce((a, b) => a + b) : 0;
var diffOdo = maxOdo - minOdo;
var diffDate = maxDate - minDate;
var divisibleCount = recordIds.length - 1;
var averageOdo = diffOdo > 0 ? (diffOdo / divisibleCount).toFixed(2) : 0;
var averageDays = diffDate > 0 ? Math.floor((diffDate / divisibleCount) / 8.64e7) : 0;
var averageSum = costSum > 0 ? (costSum / divisibleCount).toFixed(2) : 0;
costSum = costSum.toFixed(2);
Swal.fire({
title: "Record Statistics",
html: `<p>Average Distance Traveled between Records: ${averageOdo}</p>
<br />
<p>Average Days between Records: ${averageDays}</p>
<br />
<p>Total Cost: ${getGlobalConfig().currencySymbol} ${costSum}</p>
<br />
<p>Average Cost: ${getGlobalConfig().currencySymbol} ${averageSum}</p>`
,
icon: "info"
});
} }