From fb949b60e45d0a1e22fc3f9d144e9e0986939d98 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Tue, 11 Mar 2025 07:51:49 -0500 Subject: [PATCH] receipt-form: Add auto-fill feature The Add Receipt form now has a dropdown box that will automatically populate the data fields from the properties of an existing transaction. This will be helpful for the cases where the transaction was created automatically, but still needs a receipt attached to it (e.g. Wal-Mart, Target, etc. purchases and restaurants). --- js/receipt-form.ts | 60 +++++++++++++++++++++++++++++++- templates/receipt-form.html.tera | 17 ++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/js/receipt-form.ts b/js/receipt-form.ts index bd7bd24..a2a6cae 100644 --- a/js/receipt-form.ts +++ b/js/receipt-form.ts @@ -2,6 +2,9 @@ import "@shoelace-style/shoelace/dist/components/button/button.js"; import "@shoelace-style/shoelace/dist/components/details/details.js"; import "@shoelace-style/shoelace/dist/components/icon/icon.js"; import "@shoelace-style/shoelace/dist/components/input/input.js"; +import "@shoelace-style/shoelace/dist/components/option/option.js"; +import "@shoelace-style/shoelace/dist/components/spinner/spinner.js"; +import "@shoelace-style/shoelace/dist/components/select/select.js"; import "@shoelace-style/shoelace/dist/components/textarea/textarea.js"; import "./shoelace.js"; @@ -10,6 +13,7 @@ import "./camera.ts"; import CameraInput from "./camera.ts"; import SlButton from "@shoelace-style/shoelace/dist/components/button/button.js"; +import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.js"; import { notify, notifyError } from "./alert"; import { getResponseError } from "./ajaxUtil.js"; @@ -21,7 +25,10 @@ const btnUpload = form.querySelector( "sl-button[class='choose-file']", ) as SlButton; const inpImage = form.photo as HTMLInputElement; -const imgPreview = document.getElementById("upload-preview") as HTMLImageElement; +const imgPreview = document.getElementById( + "upload-preview", +) as HTMLImageElement; +const xactselect = document.getElementById("transactions") as SlSelect; form.addEventListener("submit", async (evt) => { evt.preventDefault(); @@ -89,3 +96,54 @@ inpImage.addEventListener("change", () => { } } }); + +async function fetchTransactions() { + let r: Response; + try { + r = await fetch("/transactions"); + } catch (e) { + notifyError(e.toString()); + return; + } finally { + xactselect.placeholder = ""; + xactselect.querySelectorAll("sl-spinner").forEach((e) => e.remove()); + } + if (!r.ok) { + notifyError(await getResponseError(r), 10000); + return; + } + xactselect.placeholder = "Select existing transaction"; + xactselect.disabled = false; + for (const xact of await r.json()) { + const option = document.createElement("sl-option"); + option.value = xact.id; + option.textContent = `${xact.description} • ${xact.date} • $${xact.amount}`; + option.dataset.amount = xact.amount; + option.dataset.date = xact.date.split("T")[0]; + option.dataset.vendor = xact.description; + xactselect.appendChild(option); + } +} + +xactselect.addEventListener("sl-change", () => { + const value = xactselect.value; + if (!value) { + return; + } + const option: HTMLOptionElement | null = xactselect.querySelector( + `[value="${value}"]`, + ); + if (!option) { + return; + } + form.querySelectorAll("sl-input").forEach((elm) => { + if (elm.name && elm.value !== undefined) { + const value = option.dataset[elm.name]; + if (value) { + elm.value = value; + } + } + }); +}); + +fetchTransactions(); diff --git a/templates/receipt-form.html.tera b/templates/receipt-form.html.tera index 1fc16ff..4f2ad61 100644 --- a/templates/receipt-form.html.tera +++ b/templates/receipt-form.html.tera @@ -1,5 +1,6 @@ {% extends "base" %} {% block head %} Add Receipt + {% endblock %} {% block main %}

Add Receipt

+

+ + + +

@@ -36,7 +50,7 @@

- +

@@ -47,6 +61,7 @@