From 7aa4a9ad1ae03bb437c9c16a1dfb78daa873e3bd Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Sat, 23 May 2026 05:05:23 +0000
Subject: [PATCH] Hi, Jules here! I've successfully implemented the UX feature
to add a loading state to the auth buttons. Here is a summary of the changes
I made:
- Modified `handleLogin` and `handleRegister` in `app.js` to disable the authentication button, append a loading spinner, and set `aria-busy="true"` during the async API call.
- Added CSS styles for `.btn:disabled` and `.btn[aria-busy="true"]` to reduce opacity and set `cursor: not-allowed`.
- Ensured the state resets correctly whether the network request succeeds or fails by using `finally` blocks.
Co-authored-by: singhaditya21 <53948039+singhaditya21@users.noreply.github.com>
---
web-demo/css/style.css | 7 +++++++
web-demo/js/app.js | 30 ++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/web-demo/css/style.css b/web-demo/css/style.css
index e0d4506..95aa42d 100644
--- a/web-demo/css/style.css
+++ b/web-demo/css/style.css
@@ -218,6 +218,13 @@ body {
transition: all 0.3s ease;
}
+.btn:disabled,
+.btn[aria-busy="true"] {
+ opacity: 0.7;
+ cursor: not-allowed;
+ pointer-events: none;
+}
+
.btn-primary {
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
color: white;
diff --git a/web-demo/js/app.js b/web-demo/js/app.js
index 11508de..790cab1 100644
--- a/web-demo/js/app.js
+++ b/web-demo/js/app.js
@@ -144,6 +144,15 @@ 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.disabled = true;
+ submitBtn.setAttribute('aria-busy', 'true');
+ submitBtn.innerHTML = '⌛ Logging in...';
+ }
try {
this.showToast('Logging in...', 'info');
@@ -155,6 +164,12 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Login failed', 'error');
+ } finally {
+ if (submitBtn) {
+ submitBtn.disabled = false;
+ submitBtn.removeAttribute('aria-busy');
+ submitBtn.innerHTML = originalText;
+ }
}
}
@@ -163,6 +178,15 @@ 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.disabled = true;
+ submitBtn.setAttribute('aria-busy', 'true');
+ submitBtn.innerHTML = '⌛ Creating account...';
+ }
try {
this.showToast('Creating account...', 'info');
@@ -174,6 +198,12 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Registration failed', 'error');
+ } finally {
+ if (submitBtn) {
+ submitBtn.disabled = false;
+ submitBtn.removeAttribute('aria-busy');
+ submitBtn.innerHTML = originalText;
+ }
}
}