diff options
Diffstat (limited to 'static/login-script.js')
-rw-r--r-- | static/login-script.js | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/static/login-script.js b/static/login-script.js new file mode 100644 index 0000000..da69c7b --- /dev/null +++ b/static/login-script.js @@ -0,0 +1,341 @@ +// DOM Elements +const loginForm = document.getElementById('loginForm'); +const emailInput = document.getElementById('email'); +const passwordInput = document.getElementById('password'); +const passwordToggle = document.getElementById('passwordToggle'); +const loginButton = document.getElementById('loginButton'); +const toast = document.getElementById('toast'); + +// Error message elements +const emailError = document.getElementById('emailError'); +const passwordError = document.getElementById('passwordError'); +const generalError = document.getElementById('generalError'); + +// API Configuration +const API_BASE_URL = 'https://api.taskflow.com/v1'; +const LOGIN_ENDPOINT = `${API_BASE_URL}/auth/login`; + +// Password visibility toggle +passwordToggle.addEventListener('click', () => { + const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password'; + passwordInput.setAttribute('type', type); + + const icon = passwordToggle.querySelector('.toggle-icon'); + icon.textContent = type === 'password' ? '👁️' : '🙈'; +}); + +// Form validation functions +const validateEmail = (email) => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); +}; + +const validatePassword = (password) => { + return password.length >= 6; +}; + +const showError = (element, message) => { + element.textContent = message; + element.style.display = 'block'; +}; + +const hideError = (element) => { + element.textContent = ''; + element.style.display = 'none'; +}; + +const clearAllErrors = () => { + hideError(emailError); + hideError(passwordError); + generalError.classList.remove('show'); + + emailInput.classList.remove('error'); + passwordInput.classList.remove('error'); +}; + +// Real-time validation +emailInput.addEventListener('blur', () => { + const email = emailInput.value.trim(); + if (email && !validateEmail(email)) { + showError(emailError, 'Please enter a valid email address'); + emailInput.classList.add('error'); + } else { + hideError(emailError); + emailInput.classList.remove('error'); + } +}); + +passwordInput.addEventListener('blur', () => { + const password = passwordInput.value; + if (password && !validatePassword(password)) { + showError(passwordError, 'Password must be at least 6 characters long'); + passwordInput.classList.add('error'); + } else { + hideError(passwordError); + passwordInput.classList.remove('error'); + } +}); + +// Clear errors on input +emailInput.addEventListener('input', () => { + if (emailInput.classList.contains('error')) { + hideError(emailError); + emailInput.classList.remove('error'); + } +}); + +passwordInput.addEventListener('input', () => { + if (passwordInput.classList.contains('error')) { + hideError(passwordError); + passwordInput.classList.remove('error'); + } +}); + +// Show toast notification +const showToast = (message, type = 'success') => { + const toastContent = toast.querySelector('.toast-content'); + const toastIcon = toast.querySelector('.toast-icon'); + const toastMessage = toast.querySelector('.toast-message'); + + // Set icon and color based on type + if (type === 'success') { + toastIcon.textContent = '✓'; + toast.style.background = 'var(--success)'; + } else if (type === 'error') { + toastIcon.textContent = '✕'; + toast.style.background = 'var(--error)'; + } + + toastMessage.textContent = message; + toast.classList.add('show'); + + setTimeout(() => { + toast.classList.remove('show'); + }, 4000); +}; + +// Set loading state +const setLoadingState = (loading) => { + if (loading) { + loginButton.classList.add('loading'); + loginButton.disabled = true; + } else { + loginButton.classList.remove('loading'); + loginButton.disabled = false; + } +}; + +// Make login API request +const makeLoginRequest = async (credentials) => { + try { + const response = await fetch(LOGIN_ENDPOINT, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + body: JSON.stringify(credentials) + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || `HTTP error! status: ${response.status}`); + } + + return data; + } catch (error) { + // Handle network errors or API errors + if (error.name === 'TypeError' && error.message.includes('fetch')) { + throw new Error('Network error. Please check your connection and try again.'); + } + throw error; + } +}; + +// Handle successful login +const handleLoginSuccess = (data) => { + // Store authentication data + if (data.token) { + localStorage.setItem('taskflow_token', data.token); + } + + if (data.refreshToken) { + localStorage.setItem('taskflow_refresh_token', data.refreshToken); + } + + if (data.user) { + localStorage.setItem('taskflow_user', JSON.stringify(data.user)); + } + + // Show success message + showToast('Login successful! Redirecting to dashboard...', 'success'); + + // Redirect to dashboard after a short delay + setTimeout(() => { + window.location.href = 'index.html'; // or wherever the dashboard is located + }, 2000); +}; + +// Handle login error +const handleLoginError = (error) => { + console.error('Login error:', error); + + let errorMessage = 'An unexpected error occurred. Please try again.'; + + if (error.message.includes('Invalid credentials') || + error.message.includes('Unauthorized') || + error.message.includes('401')) { + errorMessage = 'Invalid email or password. Please check your credentials and try again.'; + } else if (error.message.includes('Network error')) { + errorMessage = error.message; + } else if (error.message.includes('Too many attempts')) { + errorMessage = 'Too many login attempts. Please try again later.'; + } + + generalError.textContent = errorMessage; + generalError.classList.add('show'); + showToast(errorMessage, 'error'); +}; + +// Form submission handler +loginForm.addEventListener('submit', async (e) => { + e.preventDefault(); + + // Clear previous errors + clearAllErrors(); + + // Get form data + const email = emailInput.value.trim(); + const password = passwordInput.value; + const rememberMe = document.getElementById('rememberMe').checked; + + // Validate inputs + let hasErrors = false; + + if (!email) { + showError(emailError, 'Email is required'); + emailInput.classList.add('error'); + hasErrors = true; + } else if (!validateEmail(email)) { + showError(emailError, 'Please enter a valid email address'); + emailInput.classList.add('error'); + hasErrors = true; + } + + if (!password) { + showError(passwordError, 'Password is required'); + passwordInput.classList.add('error'); + hasErrors = true; + } else if (!validatePassword(password)) { + showError(passwordError, 'Password must be at least 6 characters long'); + passwordInput.classList.add('error'); + hasErrors = true; + } + + if (hasErrors) { + return; + } + + // Set loading state + setLoadingState(true); + + try { + // Prepare request payload + const credentials = { + email, + password, + rememberMe + }; + + // Make API request + const response = await makeLoginRequest(credentials); + + // Handle success + handleLoginSuccess(response); + + } catch (error) { + // Handle error + handleLoginError(error); + } finally { + // Remove loading state + setLoadingState(false); + } +}); + +// Social login handlers +document.querySelector('.btn-google').addEventListener('click', () => { + // In a real application, this would initiate OAuth flow + showToast('Google login would be initiated here', 'success'); + + // Simulate OAuth redirect + setTimeout(() => { + window.location.href = `${API_BASE_URL}/auth/google?redirect_uri=${encodeURIComponent(window.location.origin + '/dashboard')}`; + }, 1000); +}); + +document.querySelector('.btn-microsoft').addEventListener('click', () => { + // In a real application, this would initiate OAuth flow + showToast('Microsoft login would be initiated here', 'success'); + + // Simulate OAuth redirect + setTimeout(() => { + window.location.href = `${API_BASE_URL}/auth/microsoft?redirect_uri=${encodeURIComponent(window.location.origin + '/dashboard')}`; + }, 1000); +}); + +// Forgot password handler +document.querySelector('.forgot-password').addEventListener('click', (e) => { + e.preventDefault(); + + const email = emailInput.value.trim(); + if (email && validateEmail(email)) { + showToast(`Password reset link sent to ${email}`, 'success'); + } else { + showToast('Please enter a valid email address first', 'error'); + emailInput.focus(); + } +}); + +// Sign up link handler +document.querySelector('a[href="#signup"]').addEventListener('click', (e) => { + e.preventDefault(); + window.location.href = 'signup.html'; // Redirect to signup page +}); + +// Check if user is already logged in +window.addEventListener('load', () => { + const token = localStorage.getItem('taskflow_token'); + if (token) { + // Verify token is still valid (in a real app, you'd make an API call) + showToast('You are already logged in. Redirecting...', 'success'); + setTimeout(() => { + window.location.href = 'index.html'; + }, 2000); + } +}); + +// Demo credentials helper (for development/demo purposes) +const addDemoCredentials = () => { + const demoButton = document.createElement('button'); + demoButton.type = 'button'; + demoButton.className = 'btn btn-secondary btn-full'; + demoButton.textContent = 'Use Demo Credentials'; + demoButton.style.marginTop = '1rem'; + demoButton.style.fontSize = '0.875rem'; + + demoButton.addEventListener('click', () => { + emailInput.value = 'demo@taskflow.com'; + passwordInput.value = 'demo123'; + showToast('Demo credentials filled. Click Sign In to continue.', 'success'); + }); + + // Add after the social login section + const socialLogin = document.querySelector('.social-login'); + socialLogin.appendChild(demoButton); +}; + +// Add demo credentials in development +if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') { + addDemoCredentials(); +} |