Spaces:
Paused
Paused
| // Authentication JavaScript | |
| // API Configuration | |
| const API_BASE_URL = '/api'; | |
| // DOM Elements | |
| let currentForm = 'login'; | |
| // Initialize page | |
| document.addEventListener('DOMContentLoaded', function() { | |
| initializeAuth(); | |
| setupPasswordStrength(); | |
| setupFormValidation(); | |
| }); | |
| function initializeAuth() { | |
| // Set initial form state | |
| showLogin(); | |
| } | |
| function showLogin() { | |
| hideAllForms(); | |
| document.getElementById('loginForm').style.display = 'block'; | |
| setActiveNavButton('loginNavBtn'); | |
| currentForm = 'login'; | |
| } | |
| function showRegister() { | |
| hideAllForms(); | |
| document.getElementById('registerForm').style.display = 'block'; | |
| setActiveNavButton('registerNavBtn'); | |
| currentForm = 'register'; | |
| } | |
| function showForgotPassword() { | |
| hideAllForms(); | |
| document.getElementById('forgotPasswordForm').style.display = 'block'; | |
| clearActiveNavButtons(); | |
| currentForm = 'forgot'; | |
| } | |
| function hideAllForms() { | |
| const forms = ['loginForm', 'registerForm', 'forgotPasswordForm']; | |
| forms.forEach(formId => { | |
| document.getElementById(formId).style.display = 'none'; | |
| }); | |
| } | |
| function setActiveNavButton(activeId) { | |
| clearActiveNavButtons(); | |
| document.getElementById(activeId).classList.add('active'); | |
| } | |
| function clearActiveNavButtons() { | |
| document.querySelectorAll('.nav-btn').forEach(btn => { | |
| btn.classList.remove('active'); | |
| }); | |
| } | |
| // Password visibility toggle | |
| function togglePassword(inputId) { | |
| const input = document.getElementById(inputId); | |
| const button = input.parentElement.querySelector('.password-toggle i'); | |
| if (input.type === 'password') { | |
| input.type = 'text'; | |
| button.classList.remove('fa-eye'); | |
| button.classList.add('fa-eye-slash'); | |
| } else { | |
| input.type = 'password'; | |
| button.classList.remove('fa-eye-slash'); | |
| button.classList.add('fa-eye'); | |
| } | |
| } | |
| // Password strength checker | |
| function setupPasswordStrength() { | |
| const passwordInput = document.getElementById('registerPassword'); | |
| const strengthBar = document.querySelector('.strength-fill'); | |
| const strengthText = document.querySelector('.strength-text'); | |
| if (passwordInput) { | |
| passwordInput.addEventListener('input', function() { | |
| const password = this.value; | |
| const strength = calculatePasswordStrength(password); | |
| updatePasswordStrength(strength, strengthBar, strengthText); | |
| }); | |
| } | |
| } | |
| function calculatePasswordStrength(password) { | |
| let score = 0; | |
| let feedback = []; | |
| // Length check | |
| if (password.length >= 8) score += 20; | |
| else feedback.push('At least 8 characters'); | |
| // Uppercase check | |
| if (/[A-Z]/.test(password)) score += 20; | |
| else feedback.push('One uppercase letter'); | |
| // Lowercase check | |
| if (/[a-z]/.test(password)) score += 20; | |
| else feedback.push('One lowercase letter'); | |
| // Number check | |
| if (/\d/.test(password)) score += 20; | |
| else feedback.push('One number'); | |
| // Special character check | |
| if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) score += 20; | |
| else feedback.push('One special character'); | |
| return { score, feedback }; | |
| } | |
| function updatePasswordStrength(strength, strengthBar, strengthText) { | |
| const { score, feedback } = strength; | |
| strengthBar.style.width = score + '%'; | |
| if (score < 40) { | |
| strengthBar.style.background = '#e74c3c'; | |
| strengthText.textContent = 'Weak password'; | |
| strengthText.style.color = '#e74c3c'; | |
| } else if (score < 80) { | |
| strengthBar.style.background = '#f39c12'; | |
| strengthText.textContent = 'Medium password'; | |
| strengthText.style.color = '#f39c12'; | |
| } else { | |
| strengthBar.style.background = '#27ae60'; | |
| strengthText.textContent = 'Strong password'; | |
| strengthText.style.color = '#27ae60'; | |
| } | |
| if (feedback.length > 0 && score < 100) { | |
| strengthText.textContent += ' - Missing: ' + feedback.join(', '); | |
| } | |
| } | |
| // Form validation | |
| function setupFormValidation() { | |
| // Real-time validation for username | |
| const usernameInput = document.getElementById('registerUsername'); | |
| if (usernameInput) { | |
| usernameInput.addEventListener('input', validateUsername); | |
| usernameInput.addEventListener('blur', validateUsername); | |
| } | |
| // Real-time validation for email | |
| const emailInputs = document.querySelectorAll('input[type="email"]'); | |
| emailInputs.forEach(input => { | |
| input.addEventListener('input', validateEmail); | |
| input.addEventListener('blur', validateEmail); | |
| }); | |
| // Password confirmation validation | |
| const confirmPasswordInput = document.getElementById('confirmPassword'); | |
| if (confirmPasswordInput) { | |
| confirmPasswordInput.addEventListener('input', validatePasswordConfirmation); | |
| confirmPasswordInput.addEventListener('blur', validatePasswordConfirmation); | |
| } | |
| } | |
| function validateUsername() { | |
| const input = document.getElementById('registerUsername'); | |
| const value = input.value.trim(); | |
| const inputGroup = input.parentElement; | |
| clearValidationState(inputGroup); | |
| if (value.length === 0) return; | |
| if (value.length < 3 || value.length > 80) { | |
| setValidationState(inputGroup, 'error', 'Username must be 3-80 characters'); | |
| return false; | |
| } | |
| if (!/^[a-zA-Z0-9_-]+$/.test(value)) { | |
| setValidationState(inputGroup, 'error', 'Only letters, numbers, hyphens, and underscores allowed'); | |
| return false; | |
| } | |
| setValidationState(inputGroup, 'success'); | |
| return true; | |
| } | |
| function validateEmail() { | |
| const input = this; | |
| const value = input.value.trim(); | |
| const inputGroup = input.parentElement; | |
| clearValidationState(inputGroup); | |
| if (value.length === 0) return; | |
| const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; | |
| if (!emailRegex.test(value)) { | |
| setValidationState(inputGroup, 'error', 'Please enter a valid email address'); | |
| return false; | |
| } | |
| setValidationState(inputGroup, 'success'); | |
| return true; | |
| } | |
| function validatePasswordConfirmation() { | |
| const passwordInput = document.getElementById('registerPassword'); | |
| const confirmInput = document.getElementById('confirmPassword'); | |
| const inputGroup = confirmInput.parentElement; | |
| clearValidationState(inputGroup); | |
| if (confirmInput.value.length === 0) return; | |
| if (passwordInput.value !== confirmInput.value) { | |
| setValidationState(inputGroup, 'error', 'Passwords do not match'); | |
| return false; | |
| } | |
| setValidationState(inputGroup, 'success'); | |
| return true; | |
| } | |
| function setValidationState(inputGroup, state, message = '') { | |
| inputGroup.classList.remove('error', 'success'); | |
| inputGroup.classList.add(state); | |
| // Remove existing error message | |
| const existingError = inputGroup.parentElement.querySelector('.error-message'); | |
| if (existingError) { | |
| existingError.remove(); | |
| } | |
| // Add error message if provided | |
| if (message && state === 'error') { | |
| const errorDiv = document.createElement('div'); | |
| errorDiv.className = 'error-message'; | |
| errorDiv.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${message}`; | |
| inputGroup.parentElement.appendChild(errorDiv); | |
| } | |
| } | |
| function clearValidationState(inputGroup) { | |
| inputGroup.classList.remove('error', 'success'); | |
| const existingError = inputGroup.parentElement.querySelector('.error-message'); | |
| if (existingError) { | |
| existingError.remove(); | |
| } | |
| } | |
| // Form submission handlers | |
| async function handleLogin(event) { | |
| event.preventDefault(); | |
| const form = event.target; | |
| const formData = new FormData(form); | |
| const loginData = { | |
| login: formData.get('login'), | |
| password: formData.get('password') | |
| }; | |
| const button = document.getElementById('loginBtn'); | |
| setButtonLoading(button, true); | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/login`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(loginData) | |
| }); | |
| const result = await response.json(); | |
| if (response.ok) { | |
| // Store tokens | |
| localStorage.setItem('auth_token', result.auth_token); | |
| localStorage.setItem('refresh_token', result.refresh_token); | |
| localStorage.setItem('user_data', JSON.stringify(result.user)); | |
| showAlert('success', 'Login successful! Redirecting to dashboard...'); | |
| // Redirect to dashboard | |
| setTimeout(() => { | |
| window.location.href = '/dashboard.html'; | |
| }, 1500); | |
| } else { | |
| showAlert('error', result.error || 'Login failed'); | |
| } | |
| } catch (error) { | |
| console.error('Login error:', error); | |
| showAlert('error', 'Network error. Please try again.'); | |
| } finally { | |
| setButtonLoading(button, false); | |
| } | |
| } | |
| async function handleRegister(event) { | |
| event.preventDefault(); | |
| const form = event.target; | |
| const formData = new FormData(form); | |
| // Validate form | |
| if (!validateRegistrationForm(form)) { | |
| showAlert('error', 'Please fix the errors in the form'); | |
| return; | |
| } | |
| const registerData = { | |
| username: formData.get('username'), | |
| email: formData.get('email'), | |
| password: formData.get('password'), | |
| subscription_type: formData.get('subscriptionType') | |
| }; | |
| const button = document.getElementById('registerBtn'); | |
| setButtonLoading(button, true); | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/register`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify(registerData) | |
| }); | |
| const result = await response.json(); | |
| if (response.ok) { | |
| // Store tokens | |
| localStorage.setItem('auth_token', result.auth_token); | |
| localStorage.setItem('refresh_token', result.refresh_token); | |
| localStorage.setItem('user_data', JSON.stringify(result.user)); | |
| showAlert('success', 'Account created successfully! Redirecting to dashboard...'); | |
| // Redirect to dashboard | |
| setTimeout(() => { | |
| window.location.href = '/dashboard.html'; | |
| }, 1500); | |
| } else { | |
| showAlert('error', result.error || 'Registration failed'); | |
| } | |
| } catch (error) { | |
| console.error('Registration error:', error); | |
| showAlert('error', 'Network error. Please try again.'); | |
| } finally { | |
| setButtonLoading(button, false); | |
| } | |
| } | |
| async function handleForgotPassword(event) { | |
| event.preventDefault(); | |
| const form = event.target; | |
| const formData = new FormData(form); | |
| const email = formData.get('email'); | |
| const button = document.getElementById('forgotPasswordBtn'); | |
| setButtonLoading(button, true); | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/forgot-password`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ email }) | |
| }); | |
| const result = await response.json(); | |
| if (response.ok) { | |
| showAlert('success', 'Password reset instructions sent to your email'); | |
| // Show reset token for development (remove in production) | |
| if (result.reset_token) { | |
| showAlert('info', `Development: Reset token is ${result.reset_token}`); | |
| } | |
| // Switch back to login form | |
| setTimeout(() => { | |
| showLogin(); | |
| }, 3000); | |
| } else { | |
| showAlert('error', result.error || 'Failed to send reset instructions'); | |
| } | |
| } catch (error) { | |
| console.error('Forgot password error:', error); | |
| showAlert('error', 'Network error. Please try again.'); | |
| } finally { | |
| setButtonLoading(button, false); | |
| } | |
| } | |
| function validateRegistrationForm(form) { | |
| let isValid = true; | |
| // Validate username | |
| const usernameInput = form.querySelector('#registerUsername'); | |
| if (!validateUsername.call(usernameInput)) { | |
| isValid = false; | |
| } | |
| // Validate email | |
| const emailInput = form.querySelector('#registerEmail'); | |
| if (!validateEmail.call(emailInput)) { | |
| isValid = false; | |
| } | |
| // Validate password strength | |
| const passwordInput = form.querySelector('#registerPassword'); | |
| const strength = calculatePasswordStrength(passwordInput.value); | |
| if (strength.score < 80) { | |
| const inputGroup = passwordInput.parentElement; | |
| setValidationState(inputGroup, 'error', 'Password is too weak'); | |
| isValid = false; | |
| } | |
| // Validate password confirmation | |
| if (!validatePasswordConfirmation()) { | |
| isValid = false; | |
| } | |
| // Validate terms agreement | |
| const termsCheckbox = form.querySelector('#agreeTerms'); | |
| if (!termsCheckbox.checked) { | |
| showAlert('error', 'You must agree to the Terms of Service and Privacy Policy'); | |
| isValid = false; | |
| } | |
| return isValid; | |
| } | |
| // Utility functions | |
| function setButtonLoading(button, loading) { | |
| const btnText = button.querySelector('.btn-text'); | |
| const btnLoader = button.querySelector('.btn-loader'); | |
| if (loading) { | |
| button.classList.add('loading'); | |
| button.disabled = true; | |
| btnText.style.opacity = '0'; | |
| btnLoader.style.display = 'block'; | |
| } else { | |
| button.classList.remove('loading'); | |
| button.disabled = false; | |
| btnText.style.opacity = '1'; | |
| btnLoader.style.display = 'none'; | |
| } | |
| } | |
| function showAlert(type, message, duration = 5000) { | |
| const alertContainer = document.getElementById('alertContainer'); | |
| const alert = document.createElement('div'); | |
| alert.className = `alert ${type}`; | |
| const icon = getAlertIcon(type); | |
| alert.innerHTML = ` | |
| <i class="${icon}"></i> | |
| <span>${message}</span> | |
| <button class="alert-close" onclick="closeAlert(this)"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| `; | |
| alertContainer.appendChild(alert); | |
| // Auto-remove alert after duration | |
| setTimeout(() => { | |
| if (alert.parentElement) { | |
| closeAlert(alert.querySelector('.alert-close')); | |
| } | |
| }, duration); | |
| } | |
| function getAlertIcon(type) { | |
| const icons = { | |
| success: 'fas fa-check-circle', | |
| error: 'fas fa-exclamation-circle', | |
| warning: 'fas fa-exclamation-triangle', | |
| info: 'fas fa-info-circle' | |
| }; | |
| return icons[type] || icons.info; | |
| } | |
| function closeAlert(button) { | |
| const alert = button.parentElement; | |
| alert.style.animation = 'slideOutRight 0.3s ease-out forwards'; | |
| setTimeout(() => { | |
| if (alert.parentElement) { | |
| alert.parentElement.removeChild(alert); | |
| } | |
| }, 300); | |
| } | |
| async function verifyTokenAndRedirect(token) { | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/profile`, { | |
| headers: { | |
| 'Authorization': `Bearer ${token}` | |
| } | |
| }); | |
| if (response.ok) { | |
| // Token is valid, redirect to dashboard | |
| window.location.href = '/dashboard.html'; | |
| } else { | |
| // Token is invalid, remove from storage | |
| localStorage.removeItem('auth_token'); | |
| localStorage.removeItem('refresh_token'); | |
| localStorage.removeItem('user_data'); | |
| } | |
| } catch (error) { | |
| console.error('Token verification error:', error); | |
| // Remove invalid tokens | |
| localStorage.removeItem('auth_token'); | |
| localStorage.removeItem('refresh_token'); | |
| localStorage.removeItem('user_data'); | |
| } | |
| } | |
| // Add CSS animation for slide out | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| @keyframes slideOutRight { | |
| from { | |
| opacity: 1; | |
| transform: translateX(0); | |
| } | |
| to { | |
| opacity: 0; | |
| transform: translateX(100%); | |
| } | |
| } | |
| `; | |
| document.head.appendChild(style); | |