From 308311773047fc664833ca0564485ad5b24483d4 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sat, 6 Apr 2024 01:31:31 +1100 Subject: [PATCH] Implement custom combobox for account selection Only on balance assertion edit page currently --- drcr/journal/views.py | 6 +- drcr/static/js/combobox.js | 64 +++++++++++++++++++ .../journal/balance_assertions_edit.html | 25 +++++++- drcr/webapp.py | 4 ++ 4 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 drcr/static/js/combobox.js diff --git a/drcr/journal/views.py b/drcr/journal/views.py index 7891697..465ccf0 100644 --- a/drcr/journal/views.py +++ b/drcr/journal/views.py @@ -19,7 +19,7 @@ from flask import abort, redirect, render_template, request, url_for from .. import AMOUNT_DPS from ..database import db from ..models import Amount, Posting, Transaction, TrialBalancer -from ..webapp import all_transactions, app +from ..webapp import all_accounts, all_transactions, app from .models import BalanceAssertion from ..statements.models import StatementLineReconciliation @@ -146,7 +146,7 @@ def balance_assertions(): @app.route('/balance-assertions/new', methods=['GET', 'POST']) def balance_assertions_new(): if request.method == 'GET': - return render_template('journal/balance_assertions_edit.html', assertion=None) + return render_template('journal/balance_assertions_edit.html', assertion=None, accounts=all_accounts()) quantity = round(float(request.form['amount']) * (10**AMOUNT_DPS)) if request.form['sign'] == 'cr': @@ -172,7 +172,7 @@ def balance_assertions_edit(): abort(404) if request.method == 'GET': - return render_template('journal/balance_assertions_edit.html', assertion=assertion) + return render_template('journal/balance_assertions_edit.html', assertion=assertion, accounts=all_accounts()) if request.form.get('action', None) == 'delete': # Delete balance assertion diff --git a/drcr/static/js/combobox.js b/drcr/static/js/combobox.js new file mode 100644 index 0000000..755e65f --- /dev/null +++ b/drcr/static/js/combobox.js @@ -0,0 +1,64 @@ +/* DrCr: Web-based double-entry bookkeeping framework + Copyright (C) 2022–2024 Lee Yingtong Li (RunasSudo) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +function updateComboboxInputs(elCombobox, elInput) { + elCombobox.querySelector('ul').querySelectorAll('li').forEach((elLi) => { + const liText = elLi.querySelector('.combobox-text').innerText; + if (liText.toLowerCase().startsWith(elInput.value.toLowerCase())) { + elLi.classList.remove('hidden'); + elLi.classList.add('block'); + + if (liText == elInput.value) { + elLi.dataset.selected = 'selected'; + } else { + elLi.dataset.selected = ''; + } + } else { + elLi.classList.remove('block'); + elLi.classList.add('hidden'); + elLi.dataset.selected = ''; + } + }); +} + +// Init combobox +document.querySelectorAll('.combobox').forEach((elCombobox) => { + const elInput = elCombobox.querySelector('input'); + updateComboboxInputs(elCombobox, elInput); + + // Update combobox options on input + elInput.addEventListener('input', (evt) => { + updateComboboxInputs(elCombobox, elInput); + }); + + // Open dropdown on click button + elCombobox.querySelector('button').addEventListener('click', (evt) => { + elInput.focus(); + }); +}); + +// Update combobox value on select +document.querySelectorAll('.combobox').forEach((elCombobox) => { + const elInput = elCombobox.querySelector('input'); + elCombobox.querySelector('ul').querySelectorAll('li').forEach((elLi) => { + elLi.addEventListener('mousedown', (evt) => { + const liText = elLi.querySelector('.combobox-text').innerText; + elInput.value = liText; + updateComboboxInputs(elCombobox, elInput); + }); + }); +}); diff --git a/drcr/templates/journal/balance_assertions_edit.html b/drcr/templates/journal/balance_assertions_edit.html index 5b153de..104ec36 100644 --- a/drcr/templates/journal/balance_assertions_edit.html +++ b/drcr/templates/journal/balance_assertions_edit.html @@ -35,8 +35,25 @@ -
- +
+ + +
@@ -63,3 +80,7 @@
{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/drcr/webapp.py b/drcr/webapp.py index c922400..87436ae 100644 --- a/drcr/webapp.py +++ b/drcr/webapp.py @@ -55,6 +55,10 @@ def all_transactions(start_date=None, end_date=None, join_postings=True): return transactions +def all_accounts(): + # TODO: Can this be cached? + return sorted(list(set(p.account for t in all_transactions() for p in t.postings))) + def eofy_date(): """Get the datetime for the end of the financial year"""