first commit
This commit is contained in:
172
Views/Home/Index.cshtml
Normal file
172
Views/Home/Index.cshtml
Normal file
@@ -0,0 +1,172 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
@model string
|
||||
@{
|
||||
ViewData["Title"] = "Garage";
|
||||
}
|
||||
@section Scripts {
|
||||
<script src="~/js/garage.js?v=@StaticHelper.VersionNumber"></script>
|
||||
<script src="~/js/settings.js?v=@StaticHelper.VersionNumber"></script>
|
||||
<script src="~/js/supplyrecord.js?v=@StaticHelper.VersionNumber"></script>
|
||||
<script src="~/lib/drawdown/drawdown.js"></script>
|
||||
}
|
||||
@section Nav {
|
||||
<div class="container-fluid motovaultpro-navbar-container frosted hideOnPrint">
|
||||
<div class="row mt-2">
|
||||
<div class="d-flex motovaultpro-navbar">
|
||||
<div class="me-2" style="cursor:pointer;" onclick="returnToGarage()">
|
||||
<img src="@config.GetSmallLogoUrl()" class="motovaultpro-logo motovaultpro-tab" />
|
||||
<img src="@config.GetLogoUrl()" class="motovaultpro-logo motovaultpro-mobile-nav-show" />
|
||||
</div>
|
||||
<ul class="nav nav-tabs motovaultpro-tab flex-grow-1" id="homeTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link resizable-nav-link @(Model == "garage" ? "active" : "")" oncontextmenu="sortGarage(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><i class="bi bi-car-front"></i><span class="ms-2">@translator.Translate(userLanguage, "Garage")</span></button>
|
||||
</li>
|
||||
@if (config.GetServerEnableShopSupplies())
|
||||
{
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link resizable-nav-link" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-shop"></i><span class="ms-2">@translator.Translate(userLanguage, "Supplies")</span></button>
|
||||
</li>
|
||||
}
|
||||
@if (userConfig.ShowCalendar)
|
||||
{
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link resizable-nav-link" id="calendar-tab" data-bs-toggle="tab" data-bs-target="#calendar-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-calendar-week"></i><span class="ms-2">@translator.Translate(userLanguage, "Calendar")</span></button>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item dropdown nav-item-persist nav-item-more me-5" role="presentation" style="display:none;">
|
||||
<a class="nav-link resizable-nav-link" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false"><i class="bi bi-three-dots"></i></a>
|
||||
<ul class="dropdown-menu">
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item ms-auto" role="presentation">
|
||||
<button class="nav-link resizable-nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><i class="bi bi-gear"></i><span class="ms-2">@translator.Translate(userLanguage, "Settings")</span></button>
|
||||
</li>
|
||||
@if (User.IsInRole("CookieAuth") || User.IsInRole("APIAuth"))
|
||||
{
|
||||
<li class="nav-item dropdown nav-item-persist" role="presentation">
|
||||
<a class="nav-link resizable-nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false"><i class="bi bi-person"></i><span class="ms-2">@User.Identity.Name</span></a>
|
||||
<ul class="dropdown-menu">
|
||||
@if (User.IsInRole(nameof(UserData.IsAdmin)))
|
||||
{
|
||||
<li>
|
||||
<a class="dropdown-item" href="/Admin"><i class="bi bi-people me-2"></i>@translator.Translate(userLanguage, "Admin Panel")</a>
|
||||
</li>
|
||||
}
|
||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||
{
|
||||
<li>
|
||||
<button class="dropdown-item" onclick="showRootAccountInformationModal()"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</button>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li>
|
||||
<button class="dropdown-item" onclick="showAccountInformationModal()"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</button>
|
||||
</li>
|
||||
}
|
||||
<li>
|
||||
<button class="dropdown-item" onclick="performLogOut()"><i class="bi bi-box-arrow-right me-2"></i>@translator.Translate(userLanguage, "Logout")</button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<div class="motovaultpro-navbar-button">
|
||||
<button type="button" class="btn btn-adaptive" onclick="showMobileNav()"><i class="bi bi-list motovaultpro-menu-icon"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
}
|
||||
<div class="motovaultpro-mobile-nav" onclick="hideMobileNav()">
|
||||
<ul class="navbar-nav" id="homeTab" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link user-select-none @(Model == "garage" ? "active" : "")" ontouchstart="detectLongTouch(this)" ontouchend="detectTouchEndPremature(this)" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-car-front me-2"></i>@translator.Translate(userLanguage,"Garage")</span></button>
|
||||
</li>
|
||||
@if(config.GetServerEnableShopSupplies())
|
||||
{
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="supply-tab" data-bs-toggle="tab" data-bs-target="#supply-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-shop me-2"></i>@translator.Translate(userLanguage, "Supplies")</span></button>
|
||||
</li>
|
||||
}
|
||||
@if(userConfig.ShowCalendar){
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="calendar-tab" data-bs-toggle="tab" data-bs-target="#calendar-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-calendar-week me-2"></i>@translator.Translate(userLanguage, "Calendar")</span></button>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><span class="ms-2 display-3"><i class="bi bi-gear me-2"></i>@translator.Translate(userLanguage,"Settings")</span></button>
|
||||
</li>
|
||||
@if (User.IsInRole("CookieAuth") || User.IsInRole("APIAuth"))
|
||||
{
|
||||
@if (User.IsInRole(nameof(UserData.IsAdmin)))
|
||||
{
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="dropdown-item" href="/Admin"><span class="display-3 ms-2"><i class="bi bi-people me-2"></i>@translator.Translate(userLanguage,"Admin Panel")</span></a>
|
||||
</li>
|
||||
}
|
||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||
{
|
||||
<li>
|
||||
<button class="nav-link" onclick="showRootAccountInformationModal()"><span class="display-3 ms-2"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</span></button>
|
||||
</li>
|
||||
} else
|
||||
{
|
||||
<li>
|
||||
<button class="nav-link" onclick="showAccountInformationModal()"><span class="display-3 ms-2"><i class="bi bi-person-gear me-2"></i>@translator.Translate(userLanguage, "Profile")</span></button>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" onclick="performLogOut()"><span class="display-3 ms-2"><i class="bi bi-box-arrow-right me-2"></i>@translator.Translate(userLanguage,"Logout")</span></button>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="tab-content" id="homeTab">
|
||||
<div class="tab-pane fade @(Model == "garage" ? "show active" : "")" id="garage-tab-pane" role="tabpanel" tabindex="0">
|
||||
<div id="garageContainer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="supply-tab-pane" role="tabpanel" tabindex="0">
|
||||
</div>
|
||||
<div class="tab-pane fade" id="calendar-tab-pane" role="tabpanel" tabindex="0">
|
||||
</div>
|
||||
<div class="tab-pane fade @(Model == "settings" ? "show active" : "")" id="settings-tab-pane" role="tabpanel" tabindex="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="addVehicleModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="addVehicleModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="bulkImportModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content" id="bulkImportModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="accountInformationModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content" id="accountInformationModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stickerPrintContainer hideOnPrint">
|
||||
</div>
|
||||
<script>
|
||||
loadGarage();
|
||||
bindWindowResize();
|
||||
checkNavBarOverflow();
|
||||
</script>
|
||||
157
Views/Home/Kiosk.cshtml
Normal file
157
Views/Home/Kiosk.cshtml
Normal file
@@ -0,0 +1,157 @@
|
||||
@{
|
||||
ViewData["Title"] = "Kiosk";
|
||||
}
|
||||
@model KioskViewModel
|
||||
@section Scripts {
|
||||
<script src="~/lib/masonry/masonry.min.js"></script>
|
||||
<script src="~/lib/drawdown/drawdown.js"></script>
|
||||
}
|
||||
<div class="no-top-pad">
|
||||
<div class="progress" role="progressbar" aria-label="Refresh Progress" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100" style="height: 1px">
|
||||
<div class="progress-bar" style="width: 0%"></div>
|
||||
</div>
|
||||
<div id="kioskContainer" class="container-fluid">
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let refreshTimer;
|
||||
let exceptionList = [];
|
||||
let subtractAmount = 0;
|
||||
let kioskMode = '@Model.KioskMode';
|
||||
let currentKioskMode = 'Plan';
|
||||
let kioskWakeLock;
|
||||
|
||||
@foreach(int exception in Model.Exclusions)
|
||||
{
|
||||
@:exceptionList.push(@exception);
|
||||
}
|
||||
|
||||
function initKiosk() {
|
||||
$("body > div").removeClass("container");
|
||||
$("body > div").css('height', '100vh');
|
||||
subtractAmount = parseInt($("#kioskContainer").width() * 0.0016); //remove 0.0016% of width every 100 ms which will approximate to one minute.
|
||||
if (subtractAmount < 2) {
|
||||
subtractAmount = 2;
|
||||
}
|
||||
retrieveKioskContent();
|
||||
//acquire wakeLock;
|
||||
try {
|
||||
navigator.wakeLock.request('screen').then((wl) => {
|
||||
kioskWakeLock = wl;
|
||||
});
|
||||
} catch (err) {
|
||||
errorToast('Location Services not Enabled');
|
||||
}
|
||||
}
|
||||
function setAccessToken(accessToken){
|
||||
//use this function to never worry about user session expiring.
|
||||
$.ajaxSetup({
|
||||
headers: {
|
||||
'Authorization': `Basic ${accessToken}`
|
||||
}
|
||||
});
|
||||
console.log("Access Token for Kiosk Mode Configured!");
|
||||
}
|
||||
function retrieveKioskContent(){
|
||||
clearInterval(refreshTimer);
|
||||
if (kioskMode != 'Cycle'){
|
||||
$.post('/Home/KioskContent', { exclusions: exceptionList, kioskMode: kioskMode }, function (data) {
|
||||
$("#kioskContainer").html(data);
|
||||
$(".kiosk-content").masonry();
|
||||
if ($(".no-data-message").length == 0) {
|
||||
$(".progress-bar").width($("#kioskContainer").width());
|
||||
setTimeout(function () { startTimer() }, 500);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//cycle mode
|
||||
switch (currentKioskMode) {
|
||||
case "Vehicle":
|
||||
currentKioskMode = "Reminder";
|
||||
break;
|
||||
case "Reminder":
|
||||
currentKioskMode = "Plan";
|
||||
break;
|
||||
case "Plan":
|
||||
currentKioskMode = "Vehicle";
|
||||
break;
|
||||
}
|
||||
$.post('/Home/KioskContent', { exclusions: exceptionList, kioskMode: currentKioskMode }, function (data) {
|
||||
$("#kioskContainer").html(data);
|
||||
$(".kiosk-content").masonry();
|
||||
if ($(".no-data-message").length > 0) {
|
||||
//if no data on vehicle page
|
||||
if (currentKioskMode == "Vehicle") {
|
||||
return; //exit
|
||||
} else {
|
||||
retrieveKioskContent(); //skip until we hit a page with content.
|
||||
}
|
||||
} else {
|
||||
$(".progress-bar").width($("#kioskContainer").width());
|
||||
setTimeout(function () { startTimer() }, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
function startTimer() {
|
||||
refreshTimer = setInterval(function () {
|
||||
var currentWidth = $(".progress-bar").width();
|
||||
if (currentWidth > 0) {
|
||||
$(".progress-bar").width(currentWidth - subtractAmount);
|
||||
} else {
|
||||
retrieveKioskContent();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
function addVehicleToExceptionList(vehicleId) {
|
||||
Swal.fire({
|
||||
title: "Remove Vehicle from Dashboard?",
|
||||
text: "Removed vehicles can be restored by refreshing the page",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Remove",
|
||||
confirmButtonColor: "#dc3545"
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
exceptionList.push(vehicleId);
|
||||
if (kioskMode == 'Cycle') {
|
||||
//remove the vehicle programmatically.
|
||||
$(`[data-vehicleId=${vehicleId}]`).remove();
|
||||
} else {
|
||||
//force a refresh
|
||||
retrieveKioskContent();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function toggleReminderNote(sender){
|
||||
var reminderNote = $(sender).find('.reminder-note');
|
||||
var reminderNoteText = reminderNote.text().trim();
|
||||
if (reminderNoteText != ''){
|
||||
if (reminderNote.hasClass('d-none')) {
|
||||
reminderNote.removeClass('d-none');
|
||||
if (!reminderNote.hasClass('reminder-note-markdown')){
|
||||
let markedDownReminderNote = markdown(reminderNoteText);
|
||||
reminderNote.html(markedDownReminderNote);
|
||||
reminderNote.addClass('reminder-note-markdown');
|
||||
}
|
||||
} else {
|
||||
reminderNote.addClass('d-none');
|
||||
}
|
||||
$(".kiosk-content").masonry();
|
||||
}
|
||||
}
|
||||
function togglePlanDetails(sender) {
|
||||
toggleReminderNote(sender);
|
||||
var planSupplies = $(sender).find('.plan-supplies');
|
||||
if (planSupplies.find('.plan-supply').length > 0) {
|
||||
if (planSupplies.hasClass('d-none')) {
|
||||
planSupplies.removeClass('d-none');
|
||||
} else {
|
||||
planSupplies.addClass('d-none');
|
||||
}
|
||||
$(".kiosk-content").masonry();
|
||||
}
|
||||
}
|
||||
initKiosk();
|
||||
</script>
|
||||
276
Views/Home/Setup.cshtml
Normal file
276
Views/Home/Setup.cshtml
Normal file
@@ -0,0 +1,276 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@{
|
||||
ViewData["Title"] = "Server Settings";
|
||||
}
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
bool emailServerIsSetup = true;
|
||||
var mailConfig = config.GetMailConfig();
|
||||
var userLanguage = config.GetServerLanguage();
|
||||
if (mailConfig is null || string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
emailServerIsSetup = false;
|
||||
}
|
||||
}
|
||||
@section Scripts {
|
||||
<script src="~/js/serversettings.js?v=@StaticHelper.VersionNumber"></script>
|
||||
}
|
||||
@section Nav {
|
||||
<div class="container-fluid motovaultpro-navbar-container frosted hideOnPrint">
|
||||
<div class="row mt-2">
|
||||
<div class="d-flex motovaultpro-navbar">
|
||||
<div style="cursor:pointer;" onclick="returnToGarage()" class="me-2">
|
||||
<img src="@config.GetSmallLogoUrl()" class="motovaultpro-logo" />
|
||||
</div>
|
||||
<span class="text-truncate lead">@translator.Translate(userLanguage, "Server Settings Configurator")</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
}
|
||||
@model ServerSettingsViewModel
|
||||
<div class="setup-wizard-container frosted">
|
||||
<div class="setup-wizard-content" data-page="0">
|
||||
<div class="d-flex text-center align-items-center justify-content-center flex-column" style="height:100%;">
|
||||
<div><span class="display-5">@translator.Translate(userLanguage, "Server Settings Configurator")</span></div>
|
||||
<div><span class="lead">@translator.Translate(userLanguage, "By proceeding, you acknowledge that you are solely responsible for all consequences from utilizing the Server Settings Configurator")</span></div>
|
||||
<div class="mt-2"><a class="btn btn-primary" onclick="nextSetupPage()">@translator.Translate(userLanguage, "Acknowledge and Continue")</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setup-wizard-content" data-page="1" style="display:none;">
|
||||
<span class="display-5">@translator.Translate(userLanguage, "Server Settings")</span>
|
||||
<hr />
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="inputPostgres">@translator.Translate(userLanguage, "Postgres Connection")</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="inputPostgres" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.PostgresConnection">
|
||||
<div class="input-group-text">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="skipPostgres">
|
||||
<label class="form-check-label" for="skipPostgres">@translator.Translate(userLanguage, "Skip")</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<small class="text-body-secondary">@translator.Translate(userLanguage, "Restart Required")</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputFileExt">@translator.Translate(userLanguage, "Allowed File Extensions")</label>
|
||||
<input type="text" id="inputFileExt" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.AllowedFileExtensions">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputLogoURL">@translator.Translate(userLanguage, "Logo URL")</label>
|
||||
<input type="text" id="inputLogoURL" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.CustomLogoURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputSmallLogoURL">@translator.Translate(userLanguage, "Small Logo URL")</label>
|
||||
<input type="text" id="inputSmallLogoURL" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.CustomSmallLogoURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputMOTD">@translator.Translate(userLanguage, "Message of the Day")</label>
|
||||
<input type="text" id="inputMOTD" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.MessageOfTheDay">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputWebHook">@translator.Translate(userLanguage, "WebHook URL")</label>
|
||||
<input type="text" id="inputWebHook" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.WebHookURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputDomain">@translator.Translate(userLanguage, "Server URL")</label>
|
||||
<input type="text" id="inputDomain" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.Domain">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputCustomWidget">@translator.Translate(userLanguage, "Custom Widgets")</label>
|
||||
<select class="form-select" id="inputCustomWidget">
|
||||
<!option value="true" @(Model.CustomWidgetsEnabled ? "selected" : "")>@translator.Translate(userLanguage, "Enabled")</!option>
|
||||
<!option value="false" @(Model.CustomWidgetsEnabled ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputInvariantAPI">@translator.Translate(userLanguage, "Invariant API")</label>
|
||||
<select class="form-select" id="inputInvariantAPI">
|
||||
<!option value="true" @(Model.InvariantAPIEnabled ? "selected" : "")>@translator.Translate(userLanguage, "Enabled")</!option>
|
||||
<!option value="false" @(Model.InvariantAPIEnabled ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="setup-wizard-content" data-page="2" style="display:none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="display-5">@translator.Translate(userLanguage, "SMTP")</span>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" onchange="nextOnSkip(this)" role="switch" id="skipSMTP">
|
||||
<label class="form-check-label" for="skipSMTP">@translator.Translate(userLanguage, "Skip")</label>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="inputSMTPServer">@translator.Translate(userLanguage, "SMTP Server")</label>
|
||||
<input type="text" id="inputSMTPServer" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.SMTPConfig.EmailServer">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputSMTPPort">@translator.Translate(userLanguage, "SMTP Server Port")</label>
|
||||
<input type="text" id="inputSMTPPort" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.SMTPConfig.Port">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputSMTPFrom">@translator.Translate(userLanguage, "SMTP Sender Address")</label>
|
||||
<input type="text" id="inputSMTPFrom" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.SMTPConfig.EmailFrom">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputSMTPUsername">@translator.Translate(userLanguage, "SMTP Username")</label>
|
||||
<input type="text" id="inputSMTPUsername" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.SMTPConfig.Username">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputSMTPPassword">@translator.Translate(userLanguage, "SMTP Password")</label>
|
||||
<div class="input-group">
|
||||
<input type="password" id="inputSMTPPassword" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.SMTPConfig.Password">
|
||||
<div class="input-group-text">
|
||||
<button type="button" class="btn btn-sm text-secondary password-visible-button" onclick="togglePasswordVisibility(this)"><i class="bi bi-eye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-center flex-column">
|
||||
<div class="mt-2"><a class="btn btn-warning" onclick="sendTestEmail()">@translator.Translate(userLanguage, "Test SMTP Settings")</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="setup-wizard-content" data-page="3" style="display:none;">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="display-5">@translator.Translate(userLanguage, "Single Sign On")</span>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" onchange="nextOnSkip(this)" role="switch" id="skipOIDC">
|
||||
<label class="form-check-label" for="skipOIDC">@translator.Translate(userLanguage, "Skip")</label>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCProvider">@translator.Translate(userLanguage, "OIDC Provider")</label>
|
||||
<input type="text" id="inputOIDCProvider" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.Name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCClient">@translator.Translate(userLanguage, "OIDC Client ID")</label>
|
||||
<input type="text" id="inputOIDCClient" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.ClientId">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCSecret">@translator.Translate(userLanguage, "OIDC Client Secret")</label>
|
||||
<div class="input-group">
|
||||
<input type="password" id="inputOIDCSecret" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.ClientSecret">
|
||||
<div class="input-group-text">
|
||||
<button type="button" class="btn btn-sm text-secondary password-visible-button" onclick="togglePasswordVisibility(this)"><i class="bi bi-eye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCAuth">@translator.Translate(userLanguage, "OIDC Auth URL")</label>
|
||||
<input type="text" id="inputOIDCAuth" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.AuthURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCToken">@translator.Translate(userLanguage, "OIDC Token URL")</label>
|
||||
<input type="text" id="inputOIDCToken" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.TokenURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCUserInfo">@translator.Translate(userLanguage, "OIDC UserInfo URL")</label>
|
||||
<input type="text" id="inputOIDCUserInfo" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.UserInfoURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCRedirect">@translator.Translate(userLanguage, "OIDC Redirect URL")</label>
|
||||
<input type="text" id="inputOIDCRedirect" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.RedirectURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCScope">@translator.Translate(userLanguage, "OIDC Scope")</label>
|
||||
<input type="text" id="inputOIDCScope" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.Scope">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCLogout">@translator.Translate(userLanguage, "OIDC Logout URL")</label>
|
||||
<input type="text" id="inputOIDCLogout" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.LogOutURL">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCState">@translator.Translate(userLanguage, "OIDC Validate State")</label>
|
||||
<select class="form-select" id="inputOIDCState">
|
||||
<!option value="true" @(Model.OIDCConfig.ValidateState ? "selected" : "")>@translator.Translate(userLanguage, "Enabled")</!option>
|
||||
<!option value="false" @(Model.OIDCConfig.ValidateState ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCPKCE">@translator.Translate(userLanguage, "OIDC Use PKCE")</label>
|
||||
<select class="form-select" id="inputOIDCPKCE">
|
||||
<!option value="true" @(Model.OIDCConfig.UsePKCE ? "selected" : "")>@translator.Translate(userLanguage, "Enabled")</!option>
|
||||
<!option value="false" @(Model.OIDCConfig.UsePKCE ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCDisable">@translator.Translate(userLanguage, "OIDC Login Only")</label>
|
||||
<select class="form-select" id="inputOIDCDisable">
|
||||
<!option value="true" @(Model.OIDCConfig.DisableRegularLogin ? "selected" : "")>@translator.Translate(userLanguage, "Enabled")</!option>
|
||||
<!option value="false" @(Model.OIDCConfig.DisableRegularLogin ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="setup-wizard-content" data-page="4" style="display:none;">
|
||||
<span class="display-5">@translator.Translate(userLanguage, "Miscellaneous")</span>
|
||||
<hr />
|
||||
<form class="form-inline">
|
||||
@if (Model.EnableAuth)
|
||||
{
|
||||
<div class="form-group">
|
||||
<label for="inputRegistrationMode">@translator.Translate(userLanguage, "Registration")</label>
|
||||
<select class="form-select" id="inputRegistrationMode">
|
||||
<!option value="0" @(!Model.OpenRegistration && !Model.DisableRegistration ? "selected" : "")>@translator.Translate(userLanguage, "Invitation Only")</!option>
|
||||
<!option value="1" @(Model.DisableRegistration ? "selected" : "")>@translator.Translate(userLanguage, "Disable Registration")</!option>
|
||||
<!option value="2" @(Model.OpenRegistration ? "selected" : "")>@translator.Translate(userLanguage, "Open Registration")</!option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputOIDCRootUser">@translator.Translate(userLanguage, "Enable OIDC for Root User")</label>
|
||||
<select class="form-select" id="inputOIDCRootUser">
|
||||
<!option value="true" @(Model.EnableRootUserOIDC ? "selected" : "")>@translator.Translate(userLanguage, "Enabled")</!option>
|
||||
<!option value="false" @(Model.EnableRootUserOIDC ? "" : "selected")>@translator.Translate(userLanguage, "Disabled")</!option>
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group">
|
||||
<label for="inputDefaultReminderEmail">@translator.Translate(userLanguage, "Root User Email Address")</label>
|
||||
<input type="text" id="inputDefaultReminderEmail" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.DefaultReminderEmail">
|
||||
</div>
|
||||
<hr />
|
||||
<div class="mb-2">
|
||||
<span class="display-6">@translator.Translate(userLanguage, "Reminder Urgency Thresholds")</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputUrgentDays">@translator.Translate(userLanguage, "Urgent(Days)")</label>
|
||||
<input type="number" inputmode="numeric" id="inputUrgentDays" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.ReminderUrgencyConfig.UrgentDays">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputVeryUrgentDays">@translator.Translate(userLanguage, "Very Urgent(Days)")</label>
|
||||
<input type="number" inputmode="numeric" id="inputVeryUrgentDays" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.ReminderUrgencyConfig.VeryUrgentDays">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputUrgentDistance">@translator.Translate(userLanguage, "Urgent(Distance)")</label>
|
||||
<input type="number" inputmode="numeric" id="inputUrgentDistance" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.ReminderUrgencyConfig.UrgentDistance">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inputVeryUrgentDistance">@translator.Translate(userLanguage, "Very Urgent(Distance)")</label>
|
||||
<input type="number" inputmode="numeric" id="inputVeryUrgentDistance" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.ReminderUrgencyConfig.VeryUrgentDistance">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="setup-wizard-content" data-page="5" style="display:none;">
|
||||
<div class="d-flex text-center align-items-center justify-content-center flex-column" style="height:100%;">
|
||||
<div><span class="display-5">@translator.Translate(userLanguage, "Server Settings Saved")<span class="text-success"><i class="bi bi-check-lg"></i></span></span></div>
|
||||
<div class="mt-2"><a class="btn btn-secondary me-2" onclick="loadSetupPage(0)">@translator.Translate(userLanguage, "Restart Wizard")</a><a class="btn btn-primary" onclick="returnToGarage()">@translator.Translate(userLanguage, "Return to Garage")</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setup-wizard-nav" style="display:none;">
|
||||
<div class="d-flex align-items-center justify-content-between pe-2" style="height:100%;">
|
||||
<a class="btn btn-prev btn-link" onclick="previousSetupPage()">@translator.Translate(userLanguage, "Back")</a>
|
||||
<a class="btn btn-next btn-primary" onclick="nextSetupPage()">@translator.Translate(userLanguage, "Next")</a>
|
||||
<a class="btn btn-save btn-success" onclick="saveSetup()">@translator.Translate(userLanguage, "Save")</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
40
Views/Home/_AccountModal.cshtml
Normal file
40
Views/Home/_AccountModal.cshtml
Normal file
@@ -0,0 +1,40 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model UserData
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="updateAccountModalLabel">@translator.Translate(userLanguage, "Update Profile")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideAccountInformationModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" onkeydown="handleEnter(this)">
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="inputUsername">@translator.Translate(userLanguage, "Username")</label>
|
||||
<input type="text" id="inputUsername" class="form-control" placeholder="@translator.Translate(userLanguage, "Account Username")" value="@Model.UserName">
|
||||
<label for="inputEmail">@translator.Translate(userLanguage, "Email Address")</label>
|
||||
<input type="text" id="inputEmail" class="form-control" placeholder="@translator.Translate(userLanguage, "Email Address")" value="@Model.EmailAddress">
|
||||
<label for="inputPassword">@translator.Translate(userLanguage, "New Password")</label>
|
||||
<div class="input-group">
|
||||
<input type="password" id="inputPassword" class="form-control" placeholder="@translator.Translate(userLanguage, "New Password")" value="">
|
||||
<div class="input-group-text">
|
||||
<button type="button" class="btn btn-sm text-secondary password-visible-button" onclick="togglePasswordVisibility(this)"><i class="bi bi-eye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<label for="inputToken">@translator.Translate(userLanguage, "Token")</label>
|
||||
<input type="text" id="inputToken" class="form-control" placeholder="@translator.Translate(userLanguage, "Token")" value="">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<a onclick="generateTokenForUser()" class="btn btn-link">@translator.Translate(userLanguage, "Send Token")</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hideAccountInformationModal()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<button type="button" onclick="validateAndSaveUserAccount()" class="btn btn-primary">@translator.Translate(userLanguage, "Update")</button>
|
||||
</div>
|
||||
25
Views/Home/_Calendar.cshtml
Normal file
25
Views/Home/_Calendar.cshtml
Normal file
@@ -0,0 +1,25 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@model List<ReminderRecordViewModel>
|
||||
<script>
|
||||
var eventDates = [];
|
||||
var groupedDates = [];
|
||||
@foreach(ReminderRecordViewModel reminderRecord in Model)
|
||||
{
|
||||
@:eventDates.push({ id: @reminderRecord.Id, date: decodeHTMLEntities('@StaticHelper.GetEpochFromDateTime(reminderRecord.Date)'), description: decodeHTMLEntities('@reminderRecord.Description'), urgency: decodeHTMLEntities('@reminderRecord.Urgency.ToString()') });
|
||||
}
|
||||
</script>
|
||||
<div class="row vehicleDetailTabContainer">
|
||||
<div class="col-12 reminderCalendarView">
|
||||
<div class="reminderCalendarViewContent" style="height:60vh;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" data-bs-focus="false" id="reminderRecordCalendarModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="reminderRecordCalendarModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
initCalendar();
|
||||
</script>
|
||||
143
Views/Home/_ExtraFields.cshtml
Normal file
143
Views/Home/_ExtraFields.cshtml
Normal file
@@ -0,0 +1,143 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
@model RecordExtraField
|
||||
<script>
|
||||
var extraFields = [];
|
||||
</script>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(translator.Translate(userLanguage, "Manage Extra Fields"))</h5>
|
||||
<button type="button" class="btn-close" onclick="hideExtraFieldModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<select class="form-select" onchange="refreshExtraFieldModal()" id="extraFieldTab">
|
||||
<!option @(Model.Id == (int)ImportMode.ServiceRecord ? "selected" : "") value="@((int)ImportMode.ServiceRecord)">@translator.Translate(userLanguage, "Service Record")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.RepairRecord ? "selected" : "") value="@((int)ImportMode.RepairRecord)">@translator.Translate(userLanguage, "Repairs")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.UpgradeRecord ? "selected" : "") value="@((int)ImportMode.UpgradeRecord)">@translator.Translate(userLanguage, "Upgrades")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.GasRecord ? "selected" : "") value="@((int)ImportMode.GasRecord)">@translator.Translate(userLanguage, "Fuel")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.TaxRecord ? "selected" : "") value="@((int)ImportMode.TaxRecord)">@translator.Translate(userLanguage, "Tax")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.SupplyRecord ? "selected" : "") value="@((int)ImportMode.SupplyRecord)">@translator.Translate(userLanguage, "Supplies")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.PlanRecord ? "selected" : "") value="@((int)ImportMode.PlanRecord)">@translator.Translate(userLanguage, "Planner")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.OdometerRecord ? "selected" : "") value="@((int)ImportMode.OdometerRecord)">@translator.Translate(userLanguage, "Odometer")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.NoteRecord ? "selected" : "") value="@((int)ImportMode.NoteRecord)">@translator.Translate(userLanguage, "Notes")</!option>
|
||||
<!option @(Model.Id == (int)ImportMode.VehicleRecord ? "selected" : "") value="@((int)ImportMode.VehicleRecord)">@translator.Translate(userLanguage, "Vehicle")</!option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<table class="table table-hover">
|
||||
<thead class="sticky-top">
|
||||
<tr class="d-flex">
|
||||
<th scope="col" class="col-5">@translator.Translate(userLanguage, "Name")</th>
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Required")</th>
|
||||
<th scope="col" class="col-3">@translator.Translate(userLanguage, "Type")</th>
|
||||
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Delete")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model.ExtraFields != null)
|
||||
{
|
||||
@foreach (ExtraField extraField in Model.ExtraFields)
|
||||
{
|
||||
<script>
|
||||
extraFields.push({ name: decodeHTMLEntities('@extraField.Name'), isRequired: @extraField.IsRequired.ToString().ToLower(), fieldType: decodeHTMLEntities('@extraField.FieldType')});
|
||||
</script>
|
||||
<tr class="d-flex">
|
||||
<td class="col-5">@extraField.Name</td>
|
||||
<td class="col-2"><input class="form-check-input" type="checkbox" onchange="updateExtraFieldIsRequired(decodeHTMLEntities('@extraField.Name'), this)" value="" @(extraField.IsRequired ? "checked" : "") /></td>
|
||||
<td class="col-3">
|
||||
<select class="form-select" onchange="updateExtraFieldType(decodeHTMLEntities('@extraField.Name'), this)">
|
||||
<!option @(extraField.FieldType == ExtraFieldType.Text ? "selected" : "") value="@((int)ExtraFieldType.Text)">@translator.Translate(userLanguage, "Text")</!option>
|
||||
<!option @(extraField.FieldType == ExtraFieldType.Number ? "selected" : "") value="@((int)ExtraFieldType.Number)">@translator.Translate(userLanguage, "Number")</!option>
|
||||
<!option @(extraField.FieldType == ExtraFieldType.Decimal ? "selected" : "") value="@((int)ExtraFieldType.Decimal)">@translator.Translate(userLanguage, "Decimal")</!option>
|
||||
<!option @(extraField.FieldType == ExtraFieldType.Date ? "selected" : "") value="@((int)ExtraFieldType.Date)">@translator.Translate(userLanguage, "Date")</!option>
|
||||
<!option @(extraField.FieldType == ExtraFieldType.Time ? "selected" : "") value="@((int)ExtraFieldType.Time)">@translator.Translate(userLanguage, "Time")</!option>
|
||||
<!option @(extraField.FieldType == ExtraFieldType.Location ? "selected" : "") value="@((int)ExtraFieldType.Location)">@translator.Translate(userLanguage, "Location")</!option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="col-2"><button type="button" onclick="deleteExtraField(decodeHTMLEntities('@extraField.Name'))" class="btn btn-danger"><i class="bi bi-trash"></i></button></td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hideExtraFieldModal()">@translator.Translate(userLanguage, "Close")</button>
|
||||
<button type="button" class="btn btn-primary" onclick="addExtraField()">@translator.Translate(userLanguage, "Add New Field")</button>
|
||||
</div>
|
||||
<script>
|
||||
var importMode = @Model.Id;
|
||||
function addExtraField() {
|
||||
Swal.fire({
|
||||
title: 'Field Name',
|
||||
html: `
|
||||
<input type="text" id="inputExtraFieldName" class="swal2-input" placeholder="Field Name" onkeydown="handleSwalEnter(event)">
|
||||
`,
|
||||
confirmButtonText: 'Add',
|
||||
focusConfirm: false,
|
||||
preConfirm: () => {
|
||||
const extraFieldName = $("#inputExtraFieldName").val();
|
||||
if (!extraFieldName || extraFieldName.trim() == '') {
|
||||
Swal.showValidationMessage(`Field names cannot be blank`);
|
||||
} else if (extraFields.filter(x => x.name == extraFieldName).length > 0) {
|
||||
Swal.showValidationMessage(`Field names must be unique`);
|
||||
}
|
||||
return { extraFieldName }
|
||||
},
|
||||
}).then(function (result) {
|
||||
if (result.isConfirmed) {
|
||||
extraFields.push({ name: result.value.extraFieldName, isRequired: false});
|
||||
updateExtraFields();
|
||||
}
|
||||
});
|
||||
}
|
||||
function updateExtraFieldIsRequired(fieldId, checkbox){
|
||||
var indexToEdit = extraFields.findIndex(x => x.name == fieldId);
|
||||
var extraFieldToEdit = extraFields[indexToEdit];
|
||||
extraFieldToEdit.isRequired = $(checkbox).is(":checked");
|
||||
updateExtraFields();
|
||||
}
|
||||
function updateExtraFieldType(fieldId, dropDown){
|
||||
var indexToEdit = extraFields.findIndex(x => x.name == fieldId);
|
||||
var extraFieldToEdit = extraFields[indexToEdit];
|
||||
extraFieldToEdit.fieldType = $(dropDown).val();
|
||||
updateExtraFields();
|
||||
}
|
||||
function deleteExtraField(fieldId) {
|
||||
extraFields = extraFields.filter(x => x.name != fieldId);
|
||||
updateExtraFields();
|
||||
}
|
||||
function refreshExtraFieldModal() {
|
||||
var selectedExtraFieldTab = $("#extraFieldTab").val();
|
||||
$.post(`/Home/GetExtraFieldsModal?importMode=${selectedExtraFieldTab}`, function (data) {
|
||||
if (data) {
|
||||
$("#extraFieldModalContent").html(data);
|
||||
} else {
|
||||
errorToast(genericErrorMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
function updateExtraFields() {
|
||||
$.post('/Home/UpdateExtraFields', { id: importMode, extraFields: extraFields }, function (data) {
|
||||
if (data) {
|
||||
$("#extraFieldModalContent").html(data);
|
||||
} else {
|
||||
errorToast(genericErrorMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
89
Views/Home/_GarageDisplay.cshtml
Normal file
89
Views/Home/_GarageDisplay.cshtml
Normal file
@@ -0,0 +1,89 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model List<VehicleViewModel>
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
|
||||
}
|
||||
@if (recordTags.Any())
|
||||
{
|
||||
<div class='row'>
|
||||
<div class="col-12 d-flex align-items-center flex-wrap mb-2">
|
||||
@foreach (string recordTag in recordTags)
|
||||
{
|
||||
<span onclick="filterGarage(this)" class="user-select-none ms-1 me-1 mt-1 mb-1 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
|
||||
}
|
||||
<datalist id="tagList">
|
||||
@foreach (string recordTag in recordTags)
|
||||
{
|
||||
<!option value="@recordTag"></!option>
|
||||
}
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row gy-3 align-items-stretch vehiclesContainer pb-2 @(recordTags.Any() ? "" : "mt-2")">
|
||||
@foreach (VehicleViewModel vehicle in Model)
|
||||
{
|
||||
@if (!(userConfig.HideSoldVehicles && !string.IsNullOrWhiteSpace(vehicle.SoldDate)))
|
||||
{
|
||||
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-4 col-6 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)">
|
||||
<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%);")" />
|
||||
@if (!string.IsNullOrWhiteSpace(vehicle.SoldDate))
|
||||
{
|
||||
<div class="vehicle-sold-banner"><p class='display-6 mb-0'>@translator.Translate(userLanguage, "SOLD")</p></div>
|
||||
} else if (vehicle.DashboardMetrics.Any())
|
||||
{
|
||||
<div class="vehicle-sold-banner">
|
||||
@if (vehicle.DashboardMetrics.Contains(DashboardMetric.Default) && vehicle.LastReportedMileage != default)
|
||||
{
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<span class="ms-2"><i class="bi bi-speedometer me-2"></i>@vehicle.LastReportedMileage.ToString("N0")</span>
|
||||
</div>
|
||||
@if (vehicle.HasReminders)
|
||||
{
|
||||
<div>
|
||||
<span class="me-2"><i class="bi bi bi-bell-fill text-warning"></i></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (vehicle.DashboardMetrics.Contains(DashboardMetric.CostPerMile) && vehicle.CostPerMile != default)
|
||||
{
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<span class="ms-2"><i class="bi bi-cash-coin me-2"></i>@($"{vehicle.CostPerMile.ToString("C2")}/{vehicle.DistanceUnit}")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (vehicle.DashboardMetrics.Contains(DashboardMetric.TotalCost) && vehicle.TotalCost != default)
|
||||
{
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<span class="ms-2"><i class="bi bi-cash-coin me-2"></i>@($"{vehicle.TotalCost.ToString("C2")}")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="card-body">
|
||||
<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>
|
||||
<h5 class="card-title text-truncate">@($"{vehicle.Model}")</h5>
|
||||
<p class="card-text text-truncate">@StaticHelper.GetVehicleIdentifier(vehicle)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-4 col-6 garage-item-add user-select-none">
|
||||
<div class="card" onclick="showAddVehicleModal()" style="height:100%;">
|
||||
<img src="/defaults/addnew_vehicle.png" style="object-fit:scale-down;height:100%;pointer-events:none;" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
126
Views/Home/_Kiosk.cshtml
Normal file
126
Views/Home/_Kiosk.cshtml
Normal file
@@ -0,0 +1,126 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@model List<VehicleInfo>
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
@if (Model.Any())
|
||||
{
|
||||
<div class="row row-cols-1 row-cols-md-3 g-4 mt-1 kiosk-content" data-masonry='{"percentPosition": true }'>
|
||||
@foreach (VehicleInfo vehicle in Model)
|
||||
{
|
||||
<div class="col" data-vehicleId="@vehicle.VehicleData.Id">
|
||||
<div class="card" onclick="addVehicleToExceptionList(@vehicle.VehicleData.Id)">
|
||||
<div class="card-body" style="padding-top:0.25rem; padding-bottom:0.25rem;">
|
||||
<h5 class="card-title">@($"{vehicle.VehicleData.Year} {vehicle.VehicleData.Make} {vehicle.VehicleData.Model} ({StaticHelper.GetVehicleIdentifier(vehicle.VehicleData)})")</h5>
|
||||
<div class="row">
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.ServiceRecordCount</p>
|
||||
<p class="lead text-truncate">@translator.Translate(userLanguage, "Service")</p>
|
||||
<p class="lead text-truncate">@vehicle.ServiceRecordCost.ToString("C0")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.RepairRecordCount</p>
|
||||
<p class="lead text-truncate">@translator.Translate(userLanguage, "Repairs")</p>
|
||||
<p class="lead text-truncate">@vehicle.RepairRecordCost.ToString("C0")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.UpgradeRecordCount</p>
|
||||
<p class="lead text-truncate">@translator.Translate(userLanguage, "Upgrades")</p>
|
||||
<p class="lead text-truncate">@vehicle.UpgradeRecordCost.ToString("C0")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.GasRecordCount</p>
|
||||
<p class="lead text-truncate">@translator.Translate(userLanguage, "Fuel")</p>
|
||||
<p class="lead text-truncate">@vehicle.GasRecordCost.ToString("C0")</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (vehicle.PastDueReminderCount + vehicle.VeryUrgentReminderCount + vehicle.UrgentReminderCount + vehicle.NotUrgentReminderCount > 0)
|
||||
{
|
||||
<hr style="margin:0px;" />
|
||||
<div class="card-body" style="padding-top:0.25rem; padding-bottom:0.25rem;">
|
||||
<h5 class="card-title">@translator.Translate(userLanguage, "Reminders")</h5>
|
||||
<div class="row">
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.PastDueReminderCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Past Due")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.VeryUrgentReminderCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Very Urgent")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.UrgentReminderCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Urgent")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.NotUrgentReminderCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Not Urgent")</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (vehicle.NextReminder != null)
|
||||
{
|
||||
<hr style="margin:0px;" />
|
||||
<div class="card-body" style="padding-top:0.25rem; padding-bottom:0.25rem;">
|
||||
<h5 class="card-title">@translator.Translate(userLanguage, "Upcoming Reminder")</h5>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="display-7">@vehicle.NextReminder.Description</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, StaticHelper.GetTitleCaseReminderUrgency(vehicle.NextReminder.Urgency))</p>
|
||||
<div class="row">
|
||||
@if (vehicle.NextReminder.Metric == "Date" || vehicle.NextReminder.Metric == "Both")
|
||||
{
|
||||
<div class="col-6"><i class='bi bi-calendar-event me-2'></i>@vehicle.NextReminder.DueDate</div>
|
||||
}
|
||||
@if (vehicle.NextReminder.Metric == "Odometer" || vehicle.NextReminder.Metric == "Both")
|
||||
{
|
||||
<div class="col-6"><i class='bi bi-speedometer me-2'></i>@vehicle.NextReminder.DueOdometer</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (vehicle.PlanRecordBackLogCount + vehicle.PlanRecordInProgressCount + vehicle.PlanRecordTestingCount + vehicle.PlanRecordBackLogCount > 0)
|
||||
{
|
||||
<hr style="margin:0px;" />
|
||||
<div class="card-body" style="padding-top:0.25rem; padding-bottom:0.25rem;">
|
||||
<h5 class="card-title">@translator.Translate(userLanguage, "Plans")</h5>
|
||||
<div class="row">
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.PlanRecordBackLogCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Planned")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.PlanRecordInProgressCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Doing")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.PlanRecordTestingCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Testing")</p>
|
||||
</div>
|
||||
<div class="col-3 text-center">
|
||||
<p class="display-7">@vehicle.PlanRecordDoneCount</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, "Done")</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row no-data-message">
|
||||
<div class="col">
|
||||
<span class="display-3">@translator.Translate(userLanguage, "No records available to display")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
86
Views/Home/_KioskPlan.cshtml
Normal file
86
Views/Home/_KioskPlan.cshtml
Normal file
@@ -0,0 +1,86 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@model List<PlanRecord>
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
@if (Model.Any())
|
||||
{
|
||||
<div class="row row-cols-1 row-cols-md-3 g-4 mt-1 kiosk-content" data-masonry='{"percentPosition": true }'>
|
||||
@foreach (PlanRecord plan in Model)
|
||||
{
|
||||
<div class="col" onclick="togglePlanDetails(this)">
|
||||
<div class="card @StaticHelper.GetPlanRecordColor(plan.Priority)">
|
||||
<div class="card-body" style="padding-top:0.25rem; padding-bottom:0.25rem;">
|
||||
<h5 class="card-title">@plan.Description</h5>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="display-7 d-none reminder-note" style="white-space: pre-wrap">@plan.Notes</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, StaticHelper.GetPlanRecordProgress(plan.Progress))</p>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
@if (plan.ImportMode == ImportMode.ServiceRecord)
|
||||
{
|
||||
<span class="lead">@translator.Translate(userLanguage, "Service")</span>
|
||||
}
|
||||
else if (plan.ImportMode == ImportMode.UpgradeRecord)
|
||||
{
|
||||
<span class="lead">@translator.Translate(userLanguage, "Repairs")</span>
|
||||
}
|
||||
else if (plan.ImportMode == ImportMode.RepairRecord)
|
||||
{
|
||||
<span class="lead">@translator.Translate(userLanguage, "Upgrades")</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (plan.RequisitionHistory.Any())
|
||||
{
|
||||
<ul class="list-group list-group-flush plan-supplies d-none">
|
||||
<li class="list-group-item">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
@translator.Translate(userLanguage, "Part Number")
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@translator.Translate(userLanguage, "Description")
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@translator.Translate(userLanguage, "Quantity")
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@foreach (SupplyUsageHistory supply in plan.RequisitionHistory)
|
||||
{
|
||||
<li class="list-group-item plan-supply">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
@supply.PartNumber
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@supply.Description
|
||||
</div>
|
||||
<div class="col-4">
|
||||
@supply.Quantity
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} else
|
||||
{
|
||||
<div class="row no-data-message">
|
||||
<div class="col">
|
||||
<span class="display-3">@translator.Translate(userLanguage, "No records available to display")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
47
Views/Home/_KioskReminder.cshtml
Normal file
47
Views/Home/_KioskReminder.cshtml
Normal file
@@ -0,0 +1,47 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@model List<ReminderRecordViewModel>
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
@if (Model.Any())
|
||||
{
|
||||
<div class="row row-cols-1 row-cols-md-3 g-4 mt-1 kiosk-content" data-masonry='{"percentPosition": true }'>
|
||||
@foreach (ReminderRecordViewModel reminder in Model)
|
||||
{
|
||||
<div class="col" onclick="toggleReminderNote(this)">
|
||||
<div class="card @StaticHelper.GetReminderUrgencyColor(reminder.Urgency)">
|
||||
<div class="card-body" style="padding-top:0.25rem; padding-bottom:0.25rem;">
|
||||
<h5 class="card-title">@reminder.Description</h5>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="display-7 d-none reminder-note" style="white-space: pre-wrap">@reminder.Notes</p>
|
||||
<p class="lead text-wrap">@translator.Translate(userLanguage, StaticHelper.GetTitleCaseReminderUrgency(reminder.Urgency))</p>
|
||||
<div class="row">
|
||||
@if (reminder.Metric == ReminderMetric.Date || reminder.Metric == ReminderMetric.Both)
|
||||
{
|
||||
<div class="col-6"><i class='bi bi-calendar-event me-2'></i>@reminder.Date.ToShortDateString()</div>
|
||||
}
|
||||
@if (reminder.Metric == ReminderMetric.Odometer || reminder.Metric == ReminderMetric.Both)
|
||||
{
|
||||
<div class="col-6"><i class='bi bi-speedometer me-2'></i>@reminder.Mileage</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row no-data-message">
|
||||
<div class="col">
|
||||
<span class="display-3">@translator.Translate(userLanguage, "No records available to display")</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
39
Views/Home/_ReminderRecordCalendarModal.cshtml
Normal file
39
Views/Home/_ReminderRecordCalendarModal.cshtml
Normal file
@@ -0,0 +1,39 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model ReminderRecordViewModel
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(translator.Translate(userLanguage, "View Reminder"))</h5>
|
||||
<button type="button" class="btn-close" onclick="hideCalendarReminderModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12" id="reminderOptions">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="reminderDescription">@translator.Translate(userLanguage, "Date")</label>
|
||||
<input type="text" id="reminderDescription" readonly class="form-control" value="@Model.Date.ToShortDateString()">
|
||||
<label for="reminderDescription">@translator.Translate(userLanguage,"Description")</label>
|
||||
<input type="text" id="reminderDescription" readonly class="form-control" value="@Model.Description">
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<label for="reminderNotes">@translator.Translate(userLanguage,"Notes")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="reminderNotes" readonly class="form-control" rows="5">@Model.Notes</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" onclick="deleteCalendarReminderRecord(@Model.Id)" style="margin-right:auto;">@translator.Translate(userLanguage, "Delete")</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="hideCalendarReminderModal()">@translator.Translate(userLanguage, "Close")</button>
|
||||
@if (Model.IsRecurring && (Model.Urgency == ReminderUrgency.VeryUrgent || Model.Urgency == ReminderUrgency.PastDue))
|
||||
{
|
||||
<button type="button" class="btn btn-primary" onclick="markDoneCalendarReminderRecord(@Model.Id)">@translator.Translate(userLanguage, "Mark as Done")</button>
|
||||
}
|
||||
</div>
|
||||
31
Views/Home/_RootAccountModal.cshtml
Normal file
31
Views/Home/_RootAccountModal.cshtml
Normal file
@@ -0,0 +1,31 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model UserData
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="updateRootAccountModalLabel">@translator.Translate(userLanguage, "Update Profile")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideAccountInformationModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" onkeydown="handleEnter(this)">
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="inputUsername">@translator.Translate(userLanguage, "Username")</label>
|
||||
<input type="text" id="inputUsername" class="form-control" placeholder="@translator.Translate(userLanguage, "Account Username")" value="@Model.UserName">
|
||||
<label for="inputPassword">@translator.Translate(userLanguage, "Password")</label>
|
||||
<div class="input-group">
|
||||
<input type="password" id="inputPassword" class="form-control" placeholder="@translator.Translate(userLanguage, "Password")" value="">
|
||||
<div class="input-group-text">
|
||||
<button type="button" class="btn btn-sm text-secondary password-visible-button" onclick="togglePasswordVisibility(this)"><i class="bi bi-eye"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hideAccountInformationModal()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<button type="button" onclick="validateAndSaveRootUserAccount()" class="btn btn-primary">@translator.Translate(userLanguage, "Update")</button>
|
||||
</div>
|
||||
426
Views/Home/_Settings.cshtml
Normal file
426
Views/Home/_Settings.cshtml
Normal file
@@ -0,0 +1,426 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@model SettingsViewModel
|
||||
@inject ITranslationHelper translator
|
||||
@{
|
||||
var userLanguage = Model.UserConfig.UserLanguage;
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-6 mt-2">@translator.Translate(userLanguage, "Settings")</h6>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="col-12 col-md-6">
|
||||
<input id="preferredGasUnit" style="display:none;" value="@Model.UserConfig.PreferredGasUnit" />
|
||||
<input id="preferredFuelMileageUnit" style="display:none;" value="@Model.UserConfig.PreferredGasMileageUnit" />
|
||||
<div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateColorModeSettings(this)" type="checkbox" role="switch" id="enableDarkMode" checked="@Model.UserConfig.UseDarkMode">
|
||||
<label class="form-check-label" for="enableDarkMode">@translator.Translate(userLanguage, "Dark Mode")</label>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateColorModeSettings(this)" type="checkbox" role="switch" id="useSystemColorMode" checked="@Model.UserConfig.UseSystemColorMode">
|
||||
<label class="form-check-label" for="useSystemColorMode">@translator.Translate(userLanguage, "Adaptive Color Mode")</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableCsvImports" checked="@Model.UserConfig.EnableCsvImports">
|
||||
<label class="form-check-label" for="enableCsvImports">@translator.Translate(userLanguage, "Enable CSV Imports")</label>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useDescending" checked="@Model.UserConfig.UseDescending">
|
||||
<label class="form-check-label" for="useDescending">@translator.Translate(userLanguage, "Sort lists in Descending Order(Newest to Oldest)")</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="hideZero" checked="@Model.UserConfig.HideZero">
|
||||
<label class="form-check-label" for="hideZero">@translator.Translate(userLanguage, "Replace $0.00 Costs with ---")</label>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="automaticDecimalFormat" checked="@Model.UserConfig.AutomaticDecimalFormat">
|
||||
<label class="form-check-label" for="automaticDecimalFormat">@translator.Translate(userLanguage, "Automatically Format Decimal Inputs")</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMarkDownOnSavedNotes" checked="@Model.UserConfig.UseMarkDownOnSavedNotes">
|
||||
<label class="form-check-label" for="useMarkDownOnSavedNotes">@translator.Translate(userLanguage, "Display Saved Notes in Markdown")</label>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<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")</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="showVehicleThumbnail" checked="@Model.UserConfig.ShowVehicleThumbnail">
|
||||
<label class="form-check-label" for="showVehicleThumbnail">@translator.Translate(userLanguage, "Show Vehicle Thumbnail in Header")</label>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMPG" checked="@Model.UserConfig.UseMPG">
|
||||
<label class="form-check-label" for="useMPG">@translator.Translate(userLanguage, "Use Imperial Calculation for Fuel Economy Calculations(MPG)")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "This Will Also Change Units to Miles and Gallons")</small></label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useUKMPG" checked="@Model.UserConfig.UseUKMPG">
|
||||
<label class="form-check-label" for="useUKMPG">@translator.Translate(userLanguage, "Use UK MPG Calculation")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Input Gas Consumption in Liters, it will be converted to UK Gals for MPG Calculation")</small></label>
|
||||
</div>
|
||||
<div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimal" checked="@Model.UserConfig.UseThreeDecimalGasCost">
|
||||
<label class="form-check-label" for="useThreeDecimal">@translator.Translate(userLanguage, "Use Three Decimals For Fuel Cost")</label>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useUnitForFuelCost" checked="@Model.UserConfig.UseUnitForFuelCost">
|
||||
<label class="form-check-label" for="useUnitForFuelCost">@translator.Translate(userLanguage, "Default to Fuel Unit Cost")</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useThreeDecimalGasConsumption" checked="@Model.UserConfig.UseThreeDecimalGasConsumption">
|
||||
<label class="form-check-label" for="useThreeDecimalGasConsumption">@translator.Translate(userLanguage, "Use Three Decimals For Fuel Consumption")</label>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoReminderRefresh" checked="@Model.UserConfig.EnableAutoReminderRefresh">
|
||||
<label class="form-check-label" for="enableAutoReminderRefresh">@translator.Translate(userLanguage, "Auto Refresh Lapsed Recurring Reminders")</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAutoOdometerInsert" checked="@Model.UserConfig.EnableAutoOdometerInsert">
|
||||
<label class="form-check-label" for="enableAutoOdometerInsert">@translator.Translate(userLanguage, "Auto Insert Odometer Records")<br /><small class="text-body-secondary">@translator.Translate(userLanguage, "Only when Adding Service/Repair/Upgrade/Fuel Record or Completing a Plan")</small></label>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<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 form-check-inline @(User.IsInRole(nameof(UserData.IsRootUser)) ? "" : "d-none")">
|
||||
<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>
|
||||
</div>
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="showCalendar" checked="@Model.UserConfig.ShowCalendar">
|
||||
<label class="form-check-label" for="showCalendar">@translator.Translate(userLanguage, "Show Calendar")</label>
|
||||
</div>
|
||||
</div>
|
||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||
{
|
||||
<hr />
|
||||
<div class="form-check form-switch form-check-inline">
|
||||
<input class="form-check-input" onChange="enableAuthCheckChanged()" type="checkbox" role="switch" id="enableAuth" checked="@Model.UserConfig.EnableAuth">
|
||||
<label class="form-check-label" for="enableAuth">@translator.Translate(userLanguage, "Enable Authentication")</label>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="row" id="visibleTabs">
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-11">
|
||||
<span class="lead">@translator.Translate(userLanguage, "Visible Tabs")</span>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button onclick="showTabReorderModal()" class="btn text-secondary btn-sm"><i class="bi bi-arrow-down-up"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="ServiceRecord" id="serviceRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.ServiceRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="serviceRecordTab">@translator.Translate(userLanguage, "Service Records")</label>
|
||||
</li>
|
||||
<li class="list-group-item d-none">
|
||||
<input onChange="updateSettings()" disabled class="form-check-input me-1" type="checkbox" value="Dashboard" id="dashboardTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.Dashboard) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="dashboardTab">@translator.Translate(userLanguage, "Dashboard")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="RepairRecord" id="repairRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.RepairRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="repairRecordTab">@translator.Translate(userLanguage, "Repairs")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="UpgradeRecord" id="upgradeRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.UpgradeRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="upgradeRecordTab">@translator.Translate(userLanguage, "Upgrades")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="GasRecord" id="gasRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.GasRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="gasRecordTab">@translator.Translate(userLanguage, "Fuel")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="OdometerRecord" id="odometerRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.OdometerRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="odometerRecordTab">@translator.Translate(userLanguage, "Odometer")</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="TaxRecord" id="taxRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.TaxRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="taxRecordTab">@translator.Translate(userLanguage, "Taxes")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="NoteRecord" id="noteRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.NoteRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="noteRecordTab">@translator.Translate(userLanguage, "Notes")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="ReminderRecord" id="reminderRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.ReminderRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="reminderRecordTab">@translator.Translate(userLanguage, "Reminder")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="SupplyRecord" id="supplyRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.SupplyRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="supplyRecordTab">@translator.Translate(userLanguage, "Supplies")</label>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<input onChange="updateSettings()" class="form-check-input me-1" type="checkbox" value="PlanRecord" id="planRecordTab" @(Model.UserConfig.VisibleTabs.Contains(ImportMode.PlanRecord) ? "checked" : "")>
|
||||
<label class="form-check-label stretched-link" for="planRecordTab">@translator.Translate(userLanguage, "Planner")</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<span class="lead">@translator.Translate(userLanguage, "Default Tab")</span>
|
||||
<select class="form-select" onchange="updateSettings()" id="defaultTab">
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.Dashboard)) value="Dashboard">@translator.Translate(userLanguage, "Dashboard")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.ServiceRecord)) value="ServiceRecord">@translator.Translate(userLanguage, "Service Record")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.RepairRecord)) value="RepairRecord">@translator.Translate(userLanguage, "Repairs")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.UpgradeRecord)) value="UpgradeRecord">@translator.Translate(userLanguage, "Upgrades")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.GasRecord)) value="GasRecord">@translator.Translate(userLanguage, "Fuel")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.TaxRecord)) value="TaxRecord">@translator.Translate(userLanguage, "Tax")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.NoteRecord)) value="NoteRecord">@translator.Translate(userLanguage, "Notes")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.ReminderRecord)) value="ReminderRecord">@translator.Translate(userLanguage, "Reminders")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.SupplyRecord)) value="SupplyRecord">@translator.Translate(userLanguage, "Supplies")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.PlanRecord)) value="PlanRecord">@translator.Translate(userLanguage, "Planner")</!option>
|
||||
<!option @(StaticHelper.DefaultTabSelected(Model.UserConfig, ImportMode.OdometerRecord)) value="OdometerRecord">@translator.Translate(userLanguage, "Odometer")</!option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<span class="lead">@translator.Translate(userLanguage, "Language")</span>
|
||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||
{
|
||||
<div class="input-group">
|
||||
<select class="form-select" onchange="updateSettings()" id="defaultLanguage">
|
||||
@foreach (string uiLanguage in Model.UILanguages)
|
||||
{
|
||||
<!option value="@uiLanguage" @(Model.UserConfig.UserLanguage == uiLanguage ? "selected" : "")>@StaticHelper.GetTranslationName(uiLanguage)</!option>
|
||||
}
|
||||
</select>
|
||||
<div class="input-group-text">
|
||||
<button type="button" class="btn btn-sm btn-primary zero-y-padding" onclick="showTranslationEditor()"><i class="bi bi-pencil"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
} else
|
||||
{
|
||||
<select class="form-select" onchange="updateSettings()" id="defaultLanguage">
|
||||
@foreach (string uiLanguage in Model.UILanguages)
|
||||
{
|
||||
<!option value="@uiLanguage" @(Model.UserConfig.UserLanguage == uiLanguage ? "selected" : "")>@StaticHelper.GetTranslationName(uiLanguage)</!option>
|
||||
}
|
||||
</select>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<span class="lead">@translator.Translate(userLanguage, "Backups")</span>
|
||||
<div class="row">
|
||||
<div class="col-6 d-grid">
|
||||
<button onclick="makeBackup()" class="btn btn-primary btn-md text-truncate">@translator.Translate(userLanguage, "Create")</button>
|
||||
</div>
|
||||
<div class="col-6 d-grid">
|
||||
<input onChange="restoreBackup(this)" type="file" accept=".zip" class="d-none" id="inputBackup">
|
||||
<button onclick="openRestoreBackup()" class="btn btn-secondary btn-md text-truncate">@translator.Translate(userLanguage, "Restore")</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<span class="lead">@translator.Translate(userLanguage, "Manage Languages")</span>
|
||||
<div class="row">
|
||||
<div class="col-6 d-grid">
|
||||
<input onChange="uploadLanguage(this)" type="file" accept=".json" class="d-none" id="inputLanguage">
|
||||
<div class="btn-group">
|
||||
<button onclick="openUploadLanguage()" class="btn btn-primary btn-md text-truncate"><i class="bi bi-upload"></i><span class="ms-2 d-sm-inline d-md-none d-xl-inline">@translator.Translate(userLanguage, "Upload")</span></button>
|
||||
<button type="button" class="btn btn-md btn-primary btn-md dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#" onclick="showTranslationDownloader()">@translator.Translate(userLanguage, "Get Translations")</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-6 d-grid">
|
||||
<button onclick="deleteLanguage()" @(Model.UserConfig.UserLanguage == "en_US" ? "disabled" : "") class="btn btn-danger btn-md text-truncate"><i class="bi bi-trash"></i><span class="ms-2 d-sm-inline d-md-none d-xl-inline">@translator.Translate(userLanguage, "Delete")</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<span class="lead text-wrap">@translator.Translate(userLanguage, "Server-wide Settings")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 d-grid">
|
||||
<button onclick="showExtraFieldModal()" class="btn btn-primary btn-md text-truncate">@translator.Translate(userLanguage, "Extra Fields")</button>
|
||||
</div>
|
||||
<div class="col-6 d-grid">
|
||||
<a href="/setup" class="btn btn-secondary btn-md text-truncate">@translator.Translate(userLanguage, "Configure")</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-6 mt-2">@translator.Translate(userLanguage, "About")</h6>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="d-flex justify-content-center">
|
||||
<img src="/defaults/motovaultpro_logo.png" />
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<small class="text-body-secondary">@($"{translator.Translate(userLanguage, "Version")} {StaticHelper.VersionNumber}")</small>
|
||||
</div>
|
||||
<p class="lead">
|
||||
Developed in Madison, Wisconsin by FB Technologies LLC.
|
||||
</p>
|
||||
<p class="lead">
|
||||
If you enjoyed using this app, please consider posting and building the community.<br />
|
||||
</p>
|
||||
<p class="lead">
|
||||
This was forked from https://github.com/hargata/lubelog/<br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-7 mt-2">Open Source Dependencies</h6>
|
||||
</div>
|
||||
<p class="lead">
|
||||
MotoVaultPro utilizes open-source dependencies to serve you the best possible user experience, those dependencies are:
|
||||
</p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Bootstrap</li>
|
||||
<li class="list-group-item">Bootstrap-DatePicker</li>
|
||||
<li class="list-group-item">LiteDB</li>
|
||||
<li class="list-group-item">Npgsql</li>
|
||||
<li class="list-group-item">SweetAlert2</li>
|
||||
<li class="list-group-item">CsvHelper</li>
|
||||
<li class="list-group-item">Chart.js</li>
|
||||
<li class="list-group-item">Drawdown</li>
|
||||
<li class="list-group-item">MailKit</li>
|
||||
<li class="list-group-item">Masonry</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" id="sponsorsContainer"></div>
|
||||
<div class="modal fade" data-bs-focus="false" id="extraFieldModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="extraFieldModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" data-bs-focus="false" id="translationEditorModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="translationEditorModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" data-bs-focus="false" id="translationDownloadModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="translationDownloadModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" data-bs-focus="false" id="customWidgetModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="customWidgetModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" data-bs-focus="false" id="tabReorderModal" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content" id="tabReorderModalContent">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="translationEditorModalLabel">@translator.Translate(userLanguage, "Reorder Tabs")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideTabReorderModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<ul class="list-group motovaultpro-tab-groups">
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.Dashboard)" draggable="true" data-tab="@ImportMode.Dashboard">@translator.Translate(userLanguage, "Dashboard")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.PlanRecord)" draggable="true" data-tab="@ImportMode.PlanRecord">@translator.Translate(userLanguage, "Planner")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.OdometerRecord)" draggable="true" data-tab="@ImportMode.OdometerRecord">@translator.Translate(userLanguage, "Odometer")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.ServiceRecord)" draggable="true" data-tab="@ImportMode.ServiceRecord">@translator.Translate(userLanguage, "Service Records")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.RepairRecord)" draggable="true" data-tab="@ImportMode.RepairRecord">@translator.Translate(userLanguage, "Repairs")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.UpgradeRecord)" draggable="true" data-tab="@ImportMode.UpgradeRecord">@translator.Translate(userLanguage, "Upgrades")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.GasRecord)" draggable="true" data-tab="@ImportMode.GasRecord">@translator.Translate(userLanguage, "Fuel")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.SupplyRecord)" draggable="true" data-tab="@ImportMode.SupplyRecord">@translator.Translate(userLanguage, "Supplies")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.TaxRecord)" draggable="true" data-tab="@ImportMode.TaxRecord">@translator.Translate(userLanguage, "Taxes")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.NoteRecord)" draggable="true" data-tab="@ImportMode.NoteRecord">@translator.Translate(userLanguage, "Notes")</li>
|
||||
<li class="list-group-item" style="order: @Model.UserConfig.TabOrder.FindIndex(x=>x == ImportMode.ReminderRecord)" draggable="true" data-tab="@ImportMode.ReminderRecord">@translator.Translate(userLanguage, "Reminder")</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger me-auto" onclick="resetTabOrder()">@translator.Translate(userLanguage, "Reset Tab Order")</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="hideTabReorderModal()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<button type="button" class="btn btn-primary" onclick="updateSettings()">@translator.Translate(userLanguage, "Save Tab Order")</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function enableAuthCheckChanged() {
|
||||
var enableAuth = $("#enableAuth").is(":checked");
|
||||
if (enableAuth) {
|
||||
//swal dialog to set up username and password.
|
||||
Swal.fire({
|
||||
title: 'Setup Credentials',
|
||||
html: `
|
||||
<input type="text" id="authUsername" class="swal2-input" placeholder="Username">
|
||||
<input type="password" id="authPassword" class="swal2-input" placeholder="Password" onkeydown="handleSwalEnter(event)">
|
||||
`,
|
||||
confirmButtonText: 'Setup',
|
||||
focusConfirm: false,
|
||||
preConfirm: () => {
|
||||
const username = $("#authUsername").val();
|
||||
const password = $("#authPassword").val();
|
||||
if (!username || !password) {
|
||||
Swal.showValidationMessage(`Please enter username and password`)
|
||||
}
|
||||
return { username, password }
|
||||
},
|
||||
}).then(function (result) {
|
||||
if (result.isConfirmed) {
|
||||
$.post('/Login/CreateLoginCreds', { userName: result.value.username, password: result.value.password }, function (data) {
|
||||
if (data) {
|
||||
setTimeout(function () { window.location.href = '/Login' }, 500);
|
||||
} else {
|
||||
errorToast(genericErrorMessage());
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$.post('/Login/DestroyLoginCreds', function (data) {
|
||||
if (data) {
|
||||
setTimeout(function () { window.location.href = '/Home' }, 1000);
|
||||
} else {
|
||||
errorToast(genericErrorMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
loadSponsors();
|
||||
</script>
|
||||
70
Views/Home/_Sponsors.cshtml
Normal file
70
Views/Home/_Sponsors.cshtml
Normal file
@@ -0,0 +1,70 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@model Sponsors
|
||||
@inject ITranslationHelper translator
|
||||
@inject IConfigHelper config
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-6 mt-2">@translator.Translate(userLanguage, "Sponsors")</h6>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-center">
|
||||
<p><a class="link-body-emphasis link-offset-2 link-underline-opacity-25 link-underline-opacity-100-hover" href="https://docs.motovaultpro.com/Misc/Funding" target="_blank">Become a Sponsor</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.LifeTime.Any())
|
||||
{
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-7 mt-2">Lifetime</h6>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<p class="lead">
|
||||
@string.Join(", ", Model.LifeTime)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (Model.Gold.Any())
|
||||
{
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-7 mt-2">Gold</h6>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<p class="lead">
|
||||
@string.Join(", ", Model.Gold)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (Model.Silver.Any())
|
||||
{
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-7 mt-2">Silver</h6>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<p class="lead">
|
||||
@string.Join(", ", Model.Silver)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (Model.Bronze.Any())
|
||||
{
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h6 class="display-7 mt-2">Bronze</h6>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<p class="lead">
|
||||
@string.Join(", ", Model.Bronze)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
48
Views/Home/_TranslationEditor.cshtml
Normal file
48
Views/Home/_TranslationEditor.cshtml
Normal file
@@ -0,0 +1,48 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@inject IConfiguration serverConfig;
|
||||
@model Dictionary<string, string>
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
bool showDelete = bool.Parse(serverConfig["MOTOVAULTPRO_TRANSLATOR"] ?? "false");
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="translationEditorModalLabel">@translator.Translate(userLanguage, "Translation Editor")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideTranslationEditor()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" onkeydown="handleEnter(this)">
|
||||
<form class="form-inline">
|
||||
<div class="form-group" style="max-height:50vh; overflow-x:hidden; overflow-y:scroll;">
|
||||
@foreach(var translation in Model)
|
||||
{
|
||||
<div class="row translation-keyvalue mb-2 align-items-center">
|
||||
@if (showDelete)
|
||||
{
|
||||
<div class="col-md-1 col-1 translation-delete"><button onclick="deleteTranslationKey(this)" class="btn text-danger btn-sm"><i class="bi bi-x-lg"></i></button></div>
|
||||
<div class="col-md-5 col-11 translation-key">@translation.Key.Replace("_", " ")</div>
|
||||
} else
|
||||
{
|
||||
<div class="col-md-6 col-12 translation-key">@translation.Key.Replace("_", " ")</div>
|
||||
}
|
||||
<div class="col-md-6 col-12 translation-value">
|
||||
<textarea style="height:100%;width:98%;" class="form-control" placeholder="@translation.Value">@translation.Value</textarea>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hideTranslationEditor()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" onclick="saveTranslation()" class="btn btn-primary btn-md mt-1 mb-1">@translator.Translate(userLanguage, "Save Translation")</button>
|
||||
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden">Toggle Dropdown</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="#" onclick="exportTranslation()">@translator.Translate(userLanguage, "Export Translation")</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
76
Views/Home/_Translations.cshtml
Normal file
76
Views/Home/_Translations.cshtml
Normal file
@@ -0,0 +1,76 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model Translations
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="translationDownloaderModalLabel">@translator.Translate(userLanguage, "Available Translations")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideTranslationDownloader()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" onkeydown="handleEnter(this)">
|
||||
<form class="form-inline">
|
||||
<div class="form-group" style="max-height:50vh; overflow-x:hidden; overflow-y:scroll;">
|
||||
@foreach(var translation in Model.Africa)
|
||||
{
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">@StaticHelper.GetTranslationName(translation)</div>
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-primary" onclick="downloadTranslation('Africa','@translation')"><i class="bi bi-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@foreach (var translation in Model.Asia)
|
||||
{
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">@StaticHelper.GetTranslationName(translation)</div>
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-primary" onclick="downloadTranslation('Asia','@translation')"><i class="bi bi-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@foreach (var translation in Model.Europe)
|
||||
{
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">@StaticHelper.GetTranslationName(translation)</div>
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-primary" onclick="downloadTranslation('Europe','@translation')"><i class="bi bi-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@foreach (var translation in Model.NorthAmerica)
|
||||
{
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">@StaticHelper.GetTranslationName(translation)</div>
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-primary" onclick="downloadTranslation('NorthAmerica','@translation')"><i class="bi bi-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@foreach (var translation in Model.SouthAmerica)
|
||||
{
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">@StaticHelper.GetTranslationName(translation)</div>
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-primary" onclick="downloadTranslation('SouthAmerica','@translation')"><i class="bi bi-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@foreach (var translation in Model.Oceania)
|
||||
{
|
||||
<div class="row mb-2">
|
||||
<div class="col-10">@StaticHelper.GetTranslationName(translation)</div>
|
||||
<div class="col-2">
|
||||
<button type="button" class="btn btn-primary" onclick="downloadTranslation('Oceania','@translation')"><i class="bi bi-download"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hideTranslationDownloader()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<button type="button" class="btn btn-primary" onclick="downloadAllTranslations()">@translator.Translate(userLanguage, "Download All Translations")</button>
|
||||
</div>
|
||||
10
Views/Home/_VehicleExtraFields.cshtml
Normal file
10
Views/Home/_VehicleExtraFields.cshtml
Normal file
@@ -0,0 +1,10 @@
|
||||
@model List<ExtraField>
|
||||
@if (Model.Any())
|
||||
{
|
||||
<ul class='list-group list-group-flush'>
|
||||
@foreach (ExtraField field in Model)
|
||||
{
|
||||
<li><b>@field.Name</b> : @field.Value</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
26
Views/Home/_VehicleSelector.cshtml
Normal file
26
Views/Home/_VehicleSelector.cshtml
Normal file
@@ -0,0 +1,26 @@
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@using MotoVaultPro.Helper
|
||||
@model List<Vehicle>
|
||||
|
||||
@{
|
||||
var userLanguage = config.GetUserConfig(User).UserLanguage;
|
||||
}
|
||||
|
||||
@if (Model.Any())
|
||||
{
|
||||
<div id="vehicleSelector">
|
||||
<ul class="list-group">
|
||||
@foreach (Vehicle vehicle in Model)
|
||||
{
|
||||
<li class="list-group-item text-start">
|
||||
<input class="form-check-input" type="checkbox" value="@vehicle.Id" id="vehicleCheck_@vehicle.Id">
|
||||
<label class="form-check-label stretched-link" for="vehicleCheck_@vehicle.Id">@($"{vehicle.Year} {vehicle.Make} {vehicle.Model} ({StaticHelper.GetVehicleIdentifier(vehicle)})")</label>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
} else
|
||||
{
|
||||
<div id="vehicleSelector"><span class="lead">@translator.Translate(userLanguage, "No Vehicles Available")</span></div>
|
||||
}
|
||||
28
Views/Home/_WidgetEditor.cshtml
Normal file
28
Views/Home/_WidgetEditor.cshtml
Normal file
@@ -0,0 +1,28 @@
|
||||
@using MotoVaultPro.Helper
|
||||
@inject IConfigHelper config
|
||||
@inject ITranslationHelper translator
|
||||
@model string
|
||||
@{
|
||||
var userConfig = config.GetUserConfig(User);
|
||||
var userLanguage = userConfig.UserLanguage;
|
||||
}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="widgetEditorModalLabel">@translator.Translate(userLanguage, "Custom Widgets Editor")</h5>
|
||||
<button type="button" class="btn-close" onclick="hideCustomWidgets()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" onkeydown="handleEnter(this)">
|
||||
<form class="form-inline">
|
||||
<div class="form-group" style="height:50vh; overflow-x:hidden; overflow-y:auto;">
|
||||
<div class="row">
|
||||
<div class="col-12" style="height:48vh;">
|
||||
<textarea id="widgetEditor" style="width:100%; height:100%;">@Model</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger me-auto" onclick="deleteCustomWidgets()">@translator.Translate(userLanguage, "Delete")</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="hideCustomWidgets()">@translator.Translate(userLanguage, "Cancel")</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveCustomWidgets()">@translator.Translate(userLanguage, "Save")</button>
|
||||
</div>
|
||||
Reference in New Issue
Block a user