diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..46da24f --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1 @@ +## 2024-05-24 - Async Loading States for Form Submissions\n**Learning:** Implementing visual feedback (like disabling buttons and changing their text to 'Loading...') during async form submissions prevents double submissions and reduces user uncertainty. Wait states are essential UX elements for any async network request.\n**Action:** Add disabled styling or inline loading text to form submitters during async operations and ensure state restores in a `finally` block. diff --git a/web-demo/js/app.js b/web-demo/js/app.js index 11508de..7792b35 100644 --- a/web-demo/js/app.js +++ b/web-demo/js/app.js @@ -145,6 +145,14 @@ class ClimaAI { const email = document.getElementById('loginEmail').value; const password = document.getElementById('loginPassword').value; + const submitBtn = e.submitter; + let originalText = ''; + if (submitBtn) { + originalText = submitBtn.innerHTML; + submitBtn.disabled = true; + submitBtn.innerHTML = 'Signing In...'; + } + try { this.showToast('Logging in...', 'info'); const response = await api.login(email, password); @@ -155,6 +163,11 @@ class ClimaAI { this.checkSubscription(); } catch (error) { this.showToast(error.message || 'Login failed', 'error'); + } finally { + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = originalText; + } } } @@ -164,6 +177,14 @@ class ClimaAI { const email = document.getElementById('registerEmail').value; const password = document.getElementById('registerPassword').value; + const submitBtn = e.submitter; + let originalText = ''; + if (submitBtn) { + originalText = submitBtn.innerHTML; + submitBtn.disabled = true; + submitBtn.innerHTML = 'Signing Up...'; + } + try { this.showToast('Creating account...', 'info'); const response = await api.register(email, password, name); @@ -174,6 +195,11 @@ class ClimaAI { this.checkSubscription(); } catch (error) { this.showToast(error.message || 'Registration failed', 'error'); + } finally { + if (submitBtn) { + submitBtn.disabled = false; + submitBtn.innerHTML = originalText; + } } }