// Generic async submit + spinner for any `
`. // Replaces the standard form-POST navigation: button shows a spinner during // the request, `data-confirm` runs first (skips the action if cancelled), // page reloads on success so the new state is reflected. (() => { document.querySelectorAll('form[data-async]').forEach(form => { form.addEventListener('submit', async (e) => { e.preventDefault(); if (form.dataset.confirm && !confirm(form.dataset.confirm)) return; const btn = form.querySelector('button[type="submit"], button:not([type]), .btn-inline'); const original = btn ? btn.innerHTML : ''; if (btn) { btn.disabled = true; btn.innerHTML = ''; } try { const resp = await fetch(form.action, { method: form.method || 'POST', body: new FormData(form), redirect: 'manual', }); const ok = resp.ok || resp.type === 'opaqueredirect' || (resp.status >= 200 && resp.status < 400); if (!ok) { const text = await resp.text().catch(() => ''); alert('action failed: ' + resp.status + (text ? '\n\n' + text : '')); if (btn) { btn.disabled = false; btn.innerHTML = original; } return; } window.location.reload(); } catch (err) { alert('action failed: ' + err); if (btn) { btn.disabled = false; btn.innerHTML = original; } } }); }); })();