Replaced the function-based JavaScript account selector with the AccountSelector class that does things better.
This commit is contained in:
parent
319f0aed90
commit
a31ce3c400
@ -23,54 +23,54 @@
|
|||||||
|
|
||||||
// Initializes the page JavaScript.
|
// Initializes the page JavaScript.
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
initializeAccountSelectors();
|
AccountSelector.initialize();
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the account selectors.
|
* The account selector.
|
||||||
*
|
*
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function initializeAccountSelectors() {
|
class AccountSelector {
|
||||||
const selectors = Array.from(document.getElementsByClassName("accounting-account-selector-modal"));
|
|
||||||
|
/**
|
||||||
|
* The entry type
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
#entryType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prefix of the HTML ID and class
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
#prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an account selector.
|
||||||
|
*
|
||||||
|
* @param modal {HTMLFormElement} the account selector modal
|
||||||
|
*/
|
||||||
|
constructor(modal) {
|
||||||
|
this.#entryType = modal.dataset.entryType;
|
||||||
|
this.#prefix = "accounting-account-selector-" + modal.dataset.entryType;
|
||||||
|
this.#init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the account selector.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#init() {
|
||||||
const formAccountControl = document.getElementById("accounting-entry-form-account-control");
|
const formAccountControl = document.getElementById("accounting-entry-form-account-control");
|
||||||
const formAccount = document.getElementById("accounting-entry-form-account");
|
const formAccount = document.getElementById("accounting-entry-form-account");
|
||||||
formAccountControl.onclick = function () {
|
const more = document.getElementById(this.#prefix + "-more");
|
||||||
const entryForm = document.getElementById("accounting-entry-form");
|
const btnClear = document.getElementById(this.#prefix + "-btn-clear");
|
||||||
const prefix = "accounting-account-selector-" + entryForm.dataset.entryType;
|
const options = Array.from(document.getElementsByClassName(this.#prefix + "-option"));
|
||||||
const query = document.getElementById(prefix + "-query")
|
const selector1 = this
|
||||||
const more = document.getElementById(prefix + "-more");
|
|
||||||
const options = Array.from(document.getElementsByClassName(prefix + "-option"));
|
|
||||||
const btnClear = document.getElementById(prefix + "-btn-clear");
|
|
||||||
query.value = "";
|
|
||||||
more.classList.remove("d-none");
|
|
||||||
filterAccountOptions(prefix);
|
|
||||||
for (const option of options) {
|
|
||||||
if (option.dataset.code === formAccount.dataset.code) {
|
|
||||||
option.classList.add("active");
|
|
||||||
} else {
|
|
||||||
option.classList.remove("active");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (formAccount.dataset.code === "") {
|
|
||||||
btnClear.classList.add("btn-secondary");
|
|
||||||
btnClear.classList.remove("btn-danger");
|
|
||||||
btnClear.disabled = true;
|
|
||||||
} else {
|
|
||||||
btnClear.classList.add("btn-danger");
|
|
||||||
btnClear.classList.remove("btn-secondary");
|
|
||||||
btnClear.disabled = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (const selector of selectors) {
|
|
||||||
const more = document.getElementById(selector.dataset.prefix + "-more");
|
|
||||||
const btnClear = document.getElementById(selector.dataset.prefix + "-btn-clear");
|
|
||||||
const options = Array.from(document.getElementsByClassName(selector.dataset.prefix + "-option"));
|
|
||||||
more.onclick = function () {
|
more.onclick = function () {
|
||||||
more.classList.add("d-none");
|
more.classList.add("d-none");
|
||||||
filterAccountOptions(selector.dataset.prefix);
|
selector1.#filterAccountOptions();
|
||||||
};
|
};
|
||||||
initializeAccountQuery(selector);
|
this.#initializeAccountQuery();
|
||||||
btnClear.onclick = function () {
|
btnClear.onclick = function () {
|
||||||
formAccountControl.classList.remove("accounting-not-empty");
|
formAccountControl.classList.remove("accounting-not-empty");
|
||||||
formAccount.innerText = "";
|
formAccount.innerText = "";
|
||||||
@ -88,40 +88,36 @@ function initializeAccountSelectors() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the query on the account options.
|
* Initializes the query on the account options.
|
||||||
*
|
*
|
||||||
* @param selector {HTMLDivElement} the selector modal
|
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function initializeAccountQuery(selector) {
|
#initializeAccountQuery() {
|
||||||
const query = document.getElementById(selector.dataset.prefix + "-query");
|
const query = document.getElementById(this.#prefix + "-query");
|
||||||
|
const helper = this;
|
||||||
query.addEventListener("input", function () {
|
query.addEventListener("input", function () {
|
||||||
filterAccountOptions(selector.dataset.prefix);
|
helper.#filterAccountOptions();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters the account options.
|
* Filters the account options.
|
||||||
*
|
*
|
||||||
* @param prefix {string} the HTML ID and class prefix
|
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function filterAccountOptions(prefix) {
|
#filterAccountOptions() {
|
||||||
const query = document.getElementById(prefix + "-query");
|
const query = document.getElementById(this.#prefix + "-query");
|
||||||
const optionList = document.getElementById(prefix + "-option-list");
|
const optionList = document.getElementById(this.#prefix + "-option-list");
|
||||||
if (optionList === null) {
|
if (optionList === null) {
|
||||||
console.log(prefix + "-option-list");
|
console.log(this.#prefix + "-option-list");
|
||||||
}
|
}
|
||||||
const options = Array.from(document.getElementsByClassName(prefix + "-option"));
|
const options = Array.from(document.getElementsByClassName(this.#prefix + "-option"));
|
||||||
const more = document.getElementById(prefix + "-more");
|
const more = document.getElementById(this.#prefix + "-more");
|
||||||
const queryNoResult = document.getElementById(prefix + "-option-no-result");
|
const queryNoResult = document.getElementById(this.#prefix + "-option-no-result");
|
||||||
const codesInUse = getAccountCodeUsedInForm();
|
const codesInUse = this.#getAccountCodeUsedInForm();
|
||||||
let shouldAnyShow = false;
|
let shouldAnyShow = false;
|
||||||
for (const option of options) {
|
for (const option of options) {
|
||||||
const shouldShow = shouldAccountOptionShow(option, more, codesInUse, query);
|
const shouldShow = this.#shouldAccountOptionShow(option, more, codesInUse, query);
|
||||||
if (shouldShow) {
|
if (shouldShow) {
|
||||||
option.classList.remove("d-none");
|
option.classList.remove("d-none");
|
||||||
shouldAnyShow = true;
|
shouldAnyShow = true;
|
||||||
@ -136,9 +132,24 @@ function filterAccountOptions(prefix) {
|
|||||||
optionList.classList.remove("d-none");
|
optionList.classList.remove("d-none");
|
||||||
queryNoResult.classList.add("d-none");
|
queryNoResult.classList.add("d-none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the account codes that are used in the form.
|
||||||
|
*
|
||||||
|
* @return {string[]} the account codes that are used in the form
|
||||||
|
*/
|
||||||
|
#getAccountCodeUsedInForm() {
|
||||||
|
const accountCodes = Array.from(document.getElementsByClassName("accounting-account-code"));
|
||||||
|
const formAccount = document.getElementById("accounting-entry-form-account");
|
||||||
|
const inUse = [formAccount.dataset.code];
|
||||||
|
for (const accountCode of accountCodes) {
|
||||||
|
inUse.push(accountCode.value);
|
||||||
|
}
|
||||||
|
return inUse
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* Returns whether an account option should show.
|
* Returns whether an account option should show.
|
||||||
*
|
*
|
||||||
* @param option {HTMLLIElement} the account option
|
* @param option {HTMLLIElement} the account option
|
||||||
@ -146,9 +157,8 @@ function filterAccountOptions(prefix) {
|
|||||||
* @param inUse {string[]} the account codes that are used in the form
|
* @param inUse {string[]} the account codes that are used in the form
|
||||||
* @param query {HTMLInputElement} the query element, if any
|
* @param query {HTMLInputElement} the query element, if any
|
||||||
* @return {boolean} true if the account option should show, or false otherwise
|
* @return {boolean} true if the account option should show, or false otherwise
|
||||||
* @private
|
|
||||||
*/
|
*/
|
||||||
function shouldAccountOptionShow(option, more, inUse, query) {
|
#shouldAccountOptionShow(option, more, inUse, query) {
|
||||||
const isQueryMatched = function () {
|
const isQueryMatched = function () {
|
||||||
if (query.value === "") {
|
if (query.value === "") {
|
||||||
return true;
|
return true;
|
||||||
@ -168,20 +178,78 @@ function shouldAccountOptionShow(option, more, inUse, query) {
|
|||||||
return option.classList.contains("accounting-account-in-use") || inUse.includes(option.dataset.code);
|
return option.classList.contains("accounting-account-in-use") || inUse.includes(option.dataset.code);
|
||||||
};
|
};
|
||||||
return isMoreMatched() && isQueryMatched();
|
return isMoreMatched() && isQueryMatched();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the account codes that are used in the form.
|
|
||||||
*
|
|
||||||
* @return {string[]} the account codes that are used in the form
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function getAccountCodeUsedInForm() {
|
|
||||||
const accountCodes = Array.from(document.getElementsByClassName("accounting-account-code"));
|
|
||||||
const formAccount = document.getElementById("accounting-entry-form-account");
|
|
||||||
const inUse = [formAccount.dataset.code];
|
|
||||||
for (const accountCode of accountCodes) {
|
|
||||||
inUse.push(accountCode.value);
|
|
||||||
}
|
}
|
||||||
return inUse
|
|
||||||
|
/**
|
||||||
|
* Initializes the account selector when it is shown.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
initShow() {
|
||||||
|
const formAccount = document.getElementById("accounting-entry-form-account");
|
||||||
|
const query = document.getElementById(this.#prefix + "-query")
|
||||||
|
const more = document.getElementById(this.#prefix + "-more");
|
||||||
|
const options = Array.from(document.getElementsByClassName(this.#prefix + "-option"));
|
||||||
|
const btnClear = document.getElementById(this.#prefix + "-btn-clear");
|
||||||
|
query.value = "";
|
||||||
|
more.classList.remove("d-none");
|
||||||
|
this.#filterAccountOptions();
|
||||||
|
for (const option of options) {
|
||||||
|
if (option.dataset.code === formAccount.dataset.code) {
|
||||||
|
option.classList.add("active");
|
||||||
|
} else {
|
||||||
|
option.classList.remove("active");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (formAccount.dataset.code === "") {
|
||||||
|
btnClear.classList.add("btn-secondary");
|
||||||
|
btnClear.classList.remove("btn-danger");
|
||||||
|
btnClear.disabled = true;
|
||||||
|
} else {
|
||||||
|
btnClear.classList.add("btn-danger");
|
||||||
|
btnClear.classList.remove("btn-secondary");
|
||||||
|
btnClear.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The account selectors.
|
||||||
|
* @type {{debit: AccountSelector, credit: AccountSelector}}
|
||||||
|
*/
|
||||||
|
static #selectors = {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the account selectors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static initialize() {
|
||||||
|
const modals = Array.from(document.getElementsByClassName("accounting-account-selector-modal"));
|
||||||
|
for (const modal of modals) {
|
||||||
|
const selector = new AccountSelector(modal);
|
||||||
|
this.#selectors[selector.#entryType] = selector;
|
||||||
|
}
|
||||||
|
this.#initializeTransactionForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the transaction form.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static #initializeTransactionForm() {
|
||||||
|
const entryForm = document.getElementById("accounting-entry-form");
|
||||||
|
const formAccountControl = document.getElementById("accounting-entry-form-account-control");
|
||||||
|
const selectors = this.#selectors;
|
||||||
|
formAccountControl.onclick = function () {
|
||||||
|
selectors[entryForm.dataset.entryType].initShow();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the account selector for the journal entry form.
|
||||||
|
*x
|
||||||
|
*/
|
||||||
|
static initializeJournalEntryForm() {
|
||||||
|
const entryForm = document.getElementById("accounting-entry-form");
|
||||||
|
const formAccountControl = document.getElementById("accounting-entry-form-account-control");
|
||||||
|
formAccountControl.dataset.bsTarget = "#accounting-account-selector-" + entryForm.dataset.entryType + "-modal";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,6 @@ function initializeNewEntryButton(button) {
|
|||||||
entryForm.dataset.entryIndex = button.dataset.entryIndex;
|
entryForm.dataset.entryIndex = button.dataset.entryIndex;
|
||||||
formAccountControl.classList.remove("accounting-not-empty");
|
formAccountControl.classList.remove("accounting-not-empty");
|
||||||
formAccountControl.classList.remove("is-invalid");
|
formAccountControl.classList.remove("is-invalid");
|
||||||
formAccountControl.dataset.bsTarget = button.dataset.accountModal;
|
|
||||||
formAccount.innerText = "";
|
formAccount.innerText = "";
|
||||||
formAccount.dataset.code = "";
|
formAccount.dataset.code = "";
|
||||||
formAccount.dataset.text = "";
|
formAccount.dataset.text = "";
|
||||||
@ -181,6 +180,7 @@ function initializeNewEntryButton(button) {
|
|||||||
formAmount.value = "";
|
formAmount.value = "";
|
||||||
formAmount.classList.remove("is-invalid");
|
formAmount.classList.remove("is-invalid");
|
||||||
formAmountError.innerText = "";
|
formAmountError.innerText = "";
|
||||||
|
AccountSelector.initializeJournalEntryForm();
|
||||||
SummaryHelper.initializeNewJournalEntry(button.dataset.entryType);
|
SummaryHelper.initializeNewJournalEntry(button.dataset.entryType);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -225,7 +225,6 @@ function initializeJournalEntry(entry) {
|
|||||||
} else {
|
} else {
|
||||||
formAccountControl.classList.add("accounting-not-empty");
|
formAccountControl.classList.add("accounting-not-empty");
|
||||||
}
|
}
|
||||||
formAccountControl.dataset.bsTarget = entry.dataset.accountModal;
|
|
||||||
formAccount.innerText = accountCode.dataset.text;
|
formAccount.innerText = accountCode.dataset.text;
|
||||||
formAccount.dataset.code = accountCode.value;
|
formAccount.dataset.code = accountCode.value;
|
||||||
formAccount.dataset.text = accountCode.dataset.text;
|
formAccount.dataset.text = accountCode.dataset.text;
|
||||||
@ -238,6 +237,7 @@ function initializeJournalEntry(entry) {
|
|||||||
formSummary.dataset.value = summary.value;
|
formSummary.dataset.value = summary.value;
|
||||||
formSummary.innerText = summary.value;
|
formSummary.innerText = summary.value;
|
||||||
formAmount.value = amount.value;
|
formAmount.value = amount.value;
|
||||||
|
AccountSelector.initializeJournalEntryForm();
|
||||||
validateJournalEntryForm();
|
validateJournalEntryForm();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ First written: 2023/2/25
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="debit" data-entry-index="new" data-account-modal="#accounting-account-selector-debit-modal" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-debit-modal">
|
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="debit" data-entry-index="new" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-debit-modal">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -19,7 +19,7 @@ account-selector-modal.html: The modal for the account selector
|
|||||||
Author: imacat@mail.imacat.idv.tw (imacat)
|
Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
First written: 2023/2/25
|
First written: 2023/2/25
|
||||||
#}
|
#}
|
||||||
<div id="accounting-account-selector-{{ entry_type }}-modal" class="modal fade accounting-account-selector-modal" data-prefix="accounting-account-selector-{{ entry_type }}" tabindex="-1" aria-labelledby="accounting-account-selector-{{ entry_type }}-modal-label" aria-hidden="true">
|
<div id="accounting-account-selector-{{ entry_type }}-modal" class="modal fade accounting-account-selector-modal" data-entry-type="{{ entry_type }}" tabindex="-1" aria-labelledby="accounting-account-selector-{{ entry_type }}-modal-label" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
@ -20,7 +20,7 @@ Author: imacat@mail.imacat.idv.tw (imacat)
|
|||||||
First written: 2023/2/25
|
First written: 2023/2/25
|
||||||
#}
|
#}
|
||||||
{# <ul> For SonarQube not to complain about incorrect HTML #}
|
{# <ul> For SonarQube not to complain about incorrect HTML #}
|
||||||
<li id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}" class="list-group-item list-group-item-action d-flex justify-content-between accounting-entry accounting-currency-{{ currency_index }}-{{ entry_type }}" data-currency-index="{{ currency_index }}" data-entry-type="{{ entry_type }}" data-entry-index="{{ entry_index }}" data-account-modal="#accounting-account-selector-{{ entry_type }}-modal" data-prefix="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}">
|
<li id="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}" class="list-group-item list-group-item-action d-flex justify-content-between accounting-entry accounting-currency-{{ currency_index }}-{{ entry_type }}" data-currency-index="{{ currency_index }}" data-entry-type="{{ entry_type }}" data-entry-index="{{ entry_index }}" data-prefix="accounting-currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}">
|
||||||
{% if entry_id %}
|
{% if entry_id %}
|
||||||
<input type="hidden" name="currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-eid" value="{{ entry_id }}">
|
<input type="hidden" name="currency-{{ currency_index }}-{{ entry_type }}-{{ entry_index }}-eid" value="{{ entry_id }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -70,7 +70,7 @@ First written: 2023/2/25
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="credit" data-entry-index="new" data-account-modal="#accounting-account-selector-credit-modal" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-credit-modal">
|
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="credit" data-entry-index="new" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-credit-modal">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -72,7 +72,7 @@ First written: 2023/2/25
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="debit" data-entry-index="new" data-account-modal="#accounting-account-selector-debit-modal" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-debit-modal">
|
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="debit" data-entry-index="new" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-debit-modal">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
@ -112,7 +112,7 @@ First written: 2023/2/25
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="credit" data-entry-index="new" data-account-modal="#accounting-account-selector-credit-modal" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-credit-modal">
|
<button class="btn btn-primary accounting-btn-new-entry accounting-currency-{{ currency_index }}-btn-new-entry" type="button" data-currency-index="{{ currency_index }}" data-entry-type="credit" data-entry-index="new" data-bs-toggle="modal" data-bs-target="#accounting-summary-helper-credit-modal">
|
||||||
<i class="fas fa-plus"></i>
|
<i class="fas fa-plus"></i>
|
||||||
{{ A_("New") }}
|
{{ A_("New") }}
|
||||||
</button>
|
</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user