From ff519ea7d1cf45757ea4ec7d6d8b435e48570e99 Mon Sep 17 00:00:00 2001 From: "Edgar P. Burkhart" Date: Fri, 3 Jan 2025 18:50:50 +0100 Subject: [PATCH] Auto-fill transaction form Close #27 --- nummi/main/static/main/css/form.css | 11 +++ nummi/main/static/main/js/base.js | 70 ++++++++++++++++++- nummi/main/templates/main/form/form_base.html | 1 + nummi/transaction/forms.py | 29 ++++++++ nummi/transaction/views.py | 2 +- 5 files changed, 111 insertions(+), 2 deletions(-) diff --git a/nummi/main/static/main/css/form.css b/nummi/main/static/main/css/form.css index ec5fa90..21fc448 100644 --- a/nummi/main/static/main/css/form.css +++ b/nummi/main/static/main/css/form.css @@ -1,3 +1,11 @@ +@keyframes border-pulse { + from { + border-color: var(--green); + } + to { + } +} + form { display: grid; gap: 0.5rem; @@ -90,6 +98,9 @@ form { &:has(~ ul.errorlist) { border-color: var(--red); } + &.autocompleted:not(.mod) { + border-bottom-color: var(--green); + } &:not([type="checkbox"]) { width: 100%; diff --git a/nummi/main/static/main/js/base.js b/nummi/main/static/main/js/base.js index d4c6ea1..eaafec5 100644 --- a/nummi/main/static/main/js/base.js +++ b/nummi/main/static/main/js/base.js @@ -44,7 +44,6 @@ for (let form of forms) { ); function setIcon(event) { - console.log(input.value); icon.className = `ri-${ icons.includes(input.value) ? input.value : "square" }-line`; @@ -55,6 +54,75 @@ for (let form of forms) { setTimeout(setIcon, 0); }); } + + let ac_json = form.querySelector("#autocomplete"); + if (ac_json) { + let autocomplete_data = JSON.parse(ac_json.textContent); + let fields = form.querySelectorAll("[name]"); + function clearMod(event) { + event.target.classList.add("mod"); + event.target.removeEventListener("input", clearMod); + } + function setMod() { + for (let f of fields) { + f.addEventListener("input", clearMod); + } + } + setMod(); + form.addEventListener("reset", (event) => { + for (let f of fields) { + f.classList.remove("mod", "autocompleted"); + } + setMod(); + }); + + let old_values = []; + + let field = form.querySelector(`[name="${autocomplete_data.field}"]`); + field.addEventListener("change", (event) => { + if (field.value in autocomplete_data.data) { + old_values = []; + for (let comp of autocomplete_data.data[field.value]) { + let f = form.querySelector(`[name="${comp.field}"]`); + if (!f.classList.contains("mod")) { + old_values.push({ field: f, value: f.value }); + + f.value = comp.value; + f.dispatchEvent(new Event("input")); + f.animate([{ borderColor: "var(--green)" }, {}], 750); + f.classList.remove("mod"); + f.classList.add("autocompleted"); + f.addEventListener("input", clearMod); + function ccAutocomplete(event) { + document.removeEventListener("keyup", cancelAutocomplete); + for (comp of old_values) { + comp.field.removeEventListener("input", ccAutocomplete); + } + } + f.addEventListener("input", ccAutocomplete); + } + } + function cancelAutocomplete(event) { + if (event.code == "Escape") { + for (comp of old_values) { + let f = comp.field; + f.value = comp.value; + f.dispatchEvent(new Event("input")); + f.animate([{ borderColor: "var(--red)" }, {}], 750); + f.classList.remove("mod"); + f.classList.remove("autocompleted"); + f.addEventListener("input", clearMod); + } + document.removeEventListener("keyup", cancelAutocomplete); + } + } + document.addEventListener("keyup", cancelAutocomplete); + form.addEventListener("reset", (event) => { + document.removeEventListener("keyup", cancelAutocomplete); + }); + } + }); + } } let accounts = document.querySelector("dl.accounts"); diff --git a/nummi/main/templates/main/form/form_base.html b/nummi/main/templates/main/form/form_base.html index 4989c4b..08afb61 100644 --- a/nummi/main/templates/main/form/form_base.html +++ b/nummi/main/templates/main/form/form_base.html @@ -1,6 +1,7 @@ {% load i18n %} {% load main_extras %} {% block fields %} + {% if form.autocomplete %}{{ form.autocomplete|json_script:"autocomplete" }}{% endif %} {% if form.non_field_errors %}