Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 2024-05-18 - Missing Loading State Feedback on Async Forms
**Learning:** In the ClimaAI login and registration forms, async network requests are triggered but the submit button lacks visual loading states and remains interactive. This allows multiple submissions and leaves the user wondering if the button click registered.
**Action:** Always provide immediate visual feedback and disable the submit button during async form submissions to improve perceived performance and prevent duplicate requests. Added disabled styles and original text restoration in `app.js`.
## 2026-05-30 - Added Missing Async Form Loading States
**Learning:** Async forms natively do not provide user feedback or prevent duplicate requests during high-latency network actions. This can lead to frustration and accidental duplicate submissions.
**Action:** Handled async submit buttons by storing their original innerHTML, substituting a loading text, and applying a `disabled=true` state wrapped within a `try...finally` block. Used CSS `.btn:disabled` styling to visually communicate the locked interaction state.
7 changes: 7 additions & 0 deletions web-demo/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,13 @@ body {
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4);
}

.btn:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
box-shadow: none;
}

.btn-google {
background: white;
color: #3c4043;
Expand Down
26 changes: 26 additions & 0 deletions web-demo/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ class ClimaAI {
e.preventDefault();
const email = document.getElementById('loginEmail').value;
const password = document.getElementById('loginPassword').value;
const submitBtn = e.submitter;
let originalText = '';

if (submitBtn) {
originalText = submitBtn.innerHTML;
submitBtn.innerHTML = 'Logging in...';
submitBtn.disabled = true;
}

try {
this.showToast('Logging in...', 'info');
Expand All @@ -155,6 +163,11 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Login failed', 'error');
} finally {
if (submitBtn) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}
}

Expand All @@ -163,6 +176,14 @@ class ClimaAI {
const name = document.getElementById('registerName').value;
const email = document.getElementById('registerEmail').value;
const password = document.getElementById('registerPassword').value;
const submitBtn = e.submitter;
let originalText = '';

if (submitBtn) {
originalText = submitBtn.innerHTML;
submitBtn.innerHTML = 'Signing up...';
submitBtn.disabled = true;
}

try {
this.showToast('Creating account...', 'info');
Expand All @@ -174,6 +195,11 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Registration failed', 'error');
} finally {
if (submitBtn) {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}
}
}

Expand Down