06/01 App enhanced

This commit is contained in:
2026-06-01 20:34:21 -04:00
parent ea79ae5fdb
commit 5bdeb7b13a
5 changed files with 114 additions and 38 deletions
+55
View File
@@ -304,6 +304,61 @@ function ocrExistingReceipt(filename) {
});
}
// ── Client-side form validation ───────────────────────────────────────────────
document.getElementById('txnForm').addEventListener('submit', function(e) {
let valid = true;
const desc = document.getElementById('field_description');
if (!desc.value.trim()) {
desc.classList.add('is-invalid');
if (!desc.nextElementSibling || !desc.nextElementSibling.classList.contains('invalid-feedback')) {
const err = document.createElement('div');
err.className = 'invalid-feedback';
err.textContent = 'Description is required.';
desc.after(err);
}
valid = false;
} else {
desc.classList.remove('is-invalid');
}
const amt = document.getElementById('field_amount');
const amtVal = parseFloat(amt.value);
const amtParent = amt.closest('.input-group') || amt;
if (!amt.value || isNaN(amtVal) || amtVal <= 0) {
amt.classList.add('is-invalid');
let errEl = amtParent.parentElement.querySelector('.amt-error');
if (!errEl) {
errEl = document.createElement('div');
errEl.className = 'text-danger mt-1 amt-error';
errEl.style.fontSize = '12px';
amtParent.after(errEl);
}
errEl.textContent = amtVal < 0 ? 'Amount must be positive.' : 'Amount is required.';
valid = false;
} else {
amt.classList.remove('is-invalid');
const errEl = amtParent.parentElement.querySelector('.amt-error');
if (errEl) errEl.remove();
}
const dt = document.getElementById('field_date');
if (!dt.value) {
dt.classList.add('is-invalid');
valid = false;
} else {
dt.classList.remove('is-invalid');
}
if (!valid) e.preventDefault();
});
// Clear validation state on input
['field_description', 'field_amount', 'field_date'].forEach(id => {
const el = document.getElementById(id);
if (el) el.addEventListener('input', () => el.classList.remove('is-invalid'));
});
// ── Auto-OCR when receipt is selected in edit form ────────────────────────────
function autoOcrOnUpload(input) {
const file = input.files[0];