function calculate_total() { var total = 0 items = document.getElementsByClassName("order_item") for (const item of items){ price = parseInt(item.dataset.price) count = parseFloat(item.value) total += count * price } document.getElementById("total_price").innerText = total + "zł" } function min(a, b) { if (a > b) { return a } return b } function increase(id) { input = document.getElementById(id) step = parseFloat(input.step) input.value= min(parseFloat(input.value)+step, 0) calculate_total() } function decrease(id) { input = document.getElementById(id) step = parseFloat(input.step) input.value= min(parseFloat(input.value)-step, 0) calculate_total() } const busyText = 'Przechodzę do płatności…'; function lockForm(form) { console.log("locking form") console.log(form.querySelector('button')) console.log(form.querySelector('button[type=submit]')) const btn = form.querySelector('button[type=submit]'); form.classList.add('submitting'); form.setAttribute('aria-busy', 'true'); btn.dataset.label = btn.dataset.label || btn.textContent; btn.textContent = busyText; } function unlockForms() { document.querySelectorAll('form.submitting').forEach(form => { const btn = form.querySelector('button[type=submit]'); form.classList.remove('submitting'); form.removeAttribute('aria-busy'); if (btn.dataset.label) btn.textContent = btn.dataset.label; }); } document.body.addEventListener('htmx:beforeRequest', e => { console.log("form clicked") const form = e.target; if (!form.matches('#order-form form')) return; if (form.classList.contains('submitting')) { e.preventDefault(); return; } // blocks double submit, incl. Enter key lockForm(form); }); // Replace your existing beforeSwap handler with this one: validation errors and 5xx unlock the form document.body.addEventListener('htmx:beforeSwap', e => { if (e.detail.xhr.status >= 400) { e.detail.shouldSwap = true; e.detail.isError = false; unlockForms(); } }); // network failure or timeout: nothing gets swapped, so say something ['htmx:sendError', 'htmx:timeout'].forEach(ev => document.body.addEventListener(ev, () => { unlockForms(); const box = document.getElementById('form-errors'); if (box) box.textContent = 'Nie udało się połączyć z serwerem. Spróbuj ponownie lub zadzwoń: 690 214 515.'; }) ); // Back button from Stripe can restore the page from cache with the button still locked window.addEventListener('pageshow', e => { if (e.persisted) unlockForms(); }); htmx.config.timeout = 30000; // default is no timeout, so a hung request would spin forever calculate_total()