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
3 changes: 3 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-03-24 - Loading states on form buttons
**Learning:** Using `e.submitter` to apply a loading state on form submission is effective, but relies on `finally` blocks in async methods safely restoring state. If methods do not return Promises (e.g. `setTimeout`), the button may stay disabled forever if an error occurs.
**Action:** Always convert standard delays (like `setTimeout`) into awaitable Promises when they block form resolution, enabling safe state restoration via `try/finally` blocks and preventing lockouts.
86 changes: 64 additions & 22 deletions web-demo/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,14 @@ class ClimaAI {

async handleLogin(e) {
e.preventDefault();
const submitBtn = e.submitter;
let originalContent = '';
if (submitBtn) {
originalContent = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.style.opacity = '0.7';
submitBtn.innerHTML = 'Signing In...';
}
const email = document.getElementById('loginEmail').value;
const password = document.getElementById('loginPassword').value;

Expand All @@ -155,11 +163,25 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Login failed', 'error');
} finally {
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.style.opacity = '1';
submitBtn.innerHTML = originalContent;
}
}
}

async handleRegister(e) {
e.preventDefault();
const submitBtn = e.submitter;
let originalContent = '';
if (submitBtn) {
originalContent = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.style.opacity = '0.7';
submitBtn.innerHTML = 'Signing Up...';
}
const name = document.getElementById('registerName').value;
const email = document.getElementById('registerEmail').value;
const password = document.getElementById('registerPassword').value;
Expand All @@ -174,6 +196,12 @@ class ClimaAI {
this.checkSubscription();
} catch (error) {
this.showToast(error.message || 'Registration failed', 'error');
} finally {
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.style.opacity = '1';
submitBtn.innerHTML = originalContent;
}
}
}

Expand All @@ -186,6 +214,14 @@ class ClimaAI {
}

async handleGoogleSignIn() {
const submitBtn = document.getElementById('googleSignInBtn');
let originalContent = '';
if (submitBtn) {
originalContent = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.style.opacity = '0.7';
submitBtn.innerHTML = 'Signing in...';
}
try {
this.showToast('πŸ” Signing in with Google...', 'info');

Expand All @@ -197,31 +233,37 @@ class ClimaAI {
// 5. Backend creates/updates user and returns JWT

// For demo purposes, we'll simulate successful OAuth with demo account
setTimeout(async () => {
try {
// Auto-login with demo account
const response = await api.login('demo@climaai.com', 'Test1234');
this.user = response.user;
this.showToast('βœ… Welcome! Signed in with Google', 'success');
this.showScreen('homeScreen');
this.loadWeatherData();
this.checkSubscription();
} catch (error) {
this.showToast('Google Sign-In succeeded! Welcome!', 'success');
// Create a demo user object
this.user = {
email: 'google-user@gmail.com',
full_name: 'Google User',
is_premium: true
};
this.isPremium = true;
this.showScreen('homeScreen');
this.loadWeatherData();
}
}, 1500); // Simulate OAuth redirect delay
await new Promise(resolve => setTimeout(resolve, 1500)); // Simulate OAuth redirect delay

try {
// Auto-login with demo account
const response = await api.login('demo@climaai.com', 'Test1234');
this.user = response.user;
this.showToast('βœ… Welcome! Signed in with Google', 'success');
this.showScreen('homeScreen');
this.loadWeatherData();
this.checkSubscription();
} catch (error) {
this.showToast('Google Sign-In succeeded! Welcome!', 'success');
// Create a demo user object
this.user = {
email: 'google-user@gmail.com',
full_name: 'Google User',
is_premium: true
};
this.isPremium = true;
this.showScreen('homeScreen');
this.loadWeatherData();
}

} catch (error) {
this.showToast(error.message || 'Google Sign-In failed', 'error');
} finally {
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.style.opacity = '1';
submitBtn.innerHTML = originalContent;
}
}
}

Expand Down