const API_BASE_URL = '/api'; let currentUser = null; let resetToken = null; // Theme management function initializeTheme() { const savedTheme = localStorage.getItem('theme') || 'light'; document.documentElement.setAttribute('data-theme', savedTheme); } function toggleTheme() { const currentTheme = document.documentElement.getAttribute('data-theme') || 'light'; const newTheme = currentTheme === 'light' ? 'dark' : 'light'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); } function parseURLParams() { const urlParams = new URLSearchParams(window.location.search); const hasResetFlag = urlParams.has('reset-password'); const token = urlParams.get('token'); return { isResetMode: hasResetFlag && token, resetToken: token }; } document.addEventListener('DOMContentLoaded', function() { // Initialize theme before anything else initializeTheme(); // Ensure user section is hidden by default const userSection = document.getElementById('userSection'); if (userSection) { userSection.classList.remove('active'); userSection.style.display = 'none'; } const urlInfo = parseURLParams(); if (urlInfo.isResetMode) { resetToken = urlInfo.resetToken; showResetPasswordSection(); return; } const token = localStorage.getItem('authToken'); const userData = localStorage.getItem('userData'); if (token && userData) { try { currentUser = JSON.parse(userData); // Only show user section if we have valid user data if (currentUser && currentUser.username) { showUserSection(); } else { throw new Error('Invalid user data structure'); } } catch (e) { console.error('Invalid user data in localStorage', e); localStorage.removeItem('authToken'); localStorage.removeItem('userData'); currentUser = null; // Make sure user section stays hidden if (userSection) { userSection.classList.remove('active'); userSection.style.display = 'none'; } } } else { // No auth data, make sure user section is hidden currentUser = null; if (userSection) { userSection.classList.remove('active'); userSection.style.display = 'none'; } } const forgotPasswordLink = document.getElementById('forgotPasswordLink'); if (forgotPasswordLink) { forgotPasswordLink.addEventListener('click', handleForgotPassword); } }); function switchTab(tab) { document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); document.querySelector(`.tab:nth-child(${tab === 'login' ? '1' : '2'})`).classList.add('active'); document.querySelectorAll('.form').forEach(f => f.classList.remove('active')); document.getElementById(tab + 'Form').classList.add('active'); clearMessages(); } function clearMessages() { document.querySelectorAll('.message').forEach(msg => { msg.style.display = 'none'; msg.textContent = ''; }); } function showMessage(elementId, message, type) { const element = document.getElementById(elementId); element.textContent = message; element.className = `message ${type}`; element.style.display = 'block'; } function setButtonLoading(buttonId, loading) { const button = document.getElementById(buttonId); if (loading) { button.innerHTML = 'Processing...'; button.disabled = true; } else { if (buttonId.includes('login')) { button.innerHTML = 'Sign In'; } else if (buttonId.includes('register')) { button.innerHTML = 'Create Account'; } else if (buttonId.includes('editProfile')) { button.innerHTML = 'Update Profile'; } else if (buttonId.includes('resetPassword')) { button.innerHTML = 'Reset Password'; } button.disabled = false; } } // Only add event listeners if elements exist (for index.html) const loginForm = document.getElementById('loginForm'); if (loginForm) { loginForm.addEventListener('submit', async function(e) { e.preventDefault(); const username = document.getElementById('loginUsername').value; const password = document.getElementById('loginPassword').value; setButtonLoading('loginBtn', true); clearMessages(); try { const response = await fetch(`${API_BASE_URL}/authenticate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }); if (response.ok) { const data = await response.json(); localStorage.setItem('authToken', data.token); localStorage.setItem('userData', JSON.stringify(data)); currentUser = data; setButtonLoading('loginBtn', false); showUserSection(); } else { try { const errorData = await response.json(); const errorMessage = errorData.message || 'Login failed. Please check your credentials.'; showMessage('loginMessage', errorMessage, 'error'); } catch (parseError) { // If response is not JSON, fall back to text const errorText = await response.text(); showMessage('loginMessage', errorText || 'Login failed. Please check your credentials.', 'error'); } } } catch (error) { console.error('Login error:', error); showMessage('loginMessage', 'Network error. Please check if the auth service is running.', 'error'); } finally { setButtonLoading('loginBtn', false); } }); } const registerForm = document.getElementById('registerForm'); if (registerForm) { registerForm.addEventListener('submit', async function(e) { e.preventDefault(); const username = document.getElementById('registerUsername').value; const password = document.getElementById('registerPassword').value; const email = document.getElementById('registerEmail').value; if (username.length < 4) { showMessage('registerMessage', "Username must be at least 4 characters long.", 'error'); return; } if (password.length < 4) { showMessage('registerMessage', "Password must be at least 4 characters long.", 'error'); return; } setButtonLoading('registerBtn', true); clearMessages(); try { const response = await fetch(`${API_BASE_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, email, password }) }); if (response.ok) { const data = await response.json(); localStorage.setItem('authToken', data.token); localStorage.setItem('userData', JSON.stringify(data)); currentUser = data; setButtonLoading('registerBtn', false); showUserSection(); } else { try { const errorData = await response.json(); const errorMessage = errorData.message || 'Registration failed. Please try again.'; showMessage('registerMessage', errorMessage, 'error'); } catch (parseError) { const errorText = await response.text(); showMessage('registerMessage', errorText || 'Registration failed. Please try again.', 'error'); } } } catch (error) { console.error('Registration error:', error); showMessage('registerMessage', 'Network error. Please check if the auth service is running.', 'error'); } finally { setButtonLoading('registerBtn', false); } }); } const resetPasswordForm = document.getElementById('resetPasswordForm'); if (resetPasswordForm) { resetPasswordForm.addEventListener('submit', async function(e) { e.preventDefault(); const newPassword = document.getElementById('resetNewPassword').value; const confirmPassword = document.getElementById('resetConfirmPassword').value; if (newPassword !== confirmPassword) { showMessage('resetPasswordMessage', 'New passwords do not match.', 'error'); return; } if (newPassword.length < 4) { showMessage('resetPasswordMessage', 'New password must be at least 4 characters long.', 'error'); return; } setButtonLoading('resetPasswordBtn', true); clearResetPasswordMessages(); try { const response = await fetch(`${API_BASE_URL}/users/reset-password`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + resetToken }, body: JSON.stringify({ newPassword: newPassword }) }); if (response.ok) { showMessage('resetPasswordMessage', 'Password reset successfully!', 'success'); document.getElementById('resetPasswordForm').reset(); setTimeout(() => { window.location.href = window.location.pathname; }, 1000); } else { try { const errorData = await response.json(); const errorMessage = errorData.message || 'Failed to reset password. Please try again.'; showMessage('resetPasswordMessage', errorMessage, 'error'); } catch (parseError) { // If response is not JSON, fall back to text const errorText = await response.text(); showMessage('resetPasswordMessage', errorText || 'Failed to reset password. Please try again.', 'error'); } } } catch (error) { console.error('Reset password error:', error); showMessage('resetPasswordMessage', 'Network error. Please check if the auth service is running.', 'error'); } finally { setButtonLoading('resetPasswordBtn', false); } }); } const editProfileForm = document.getElementById('editProfileForm'); if (editProfileForm) { editProfileForm.addEventListener('submit', async function(e) { e.preventDefault(); const currentPassword = document.getElementById('currentPassword').value; const newPassword = document.getElementById('newPassword').value; const confirmPassword = document.getElementById('confirmPassword').value; const email = document.getElementById('editEmail').value; // Validate that at least one field is being updated const isPasswordChange = newPassword.trim() !== ''; const currentEmail = currentUser.email || ''; const newEmail = email.trim(); const isEmailChange = newEmail !== currentEmail; if (!isPasswordChange && !isEmailChange) { showMessage('editProfileMessage', 'Please make at least one change: update your email address or change your password.', 'error'); return; } if (isPasswordChange && newPassword !== confirmPassword) { showMessage('editProfileMessage', 'New passwords do not match.', 'error'); return; } if (isPasswordChange && newPassword.length < 4) { showMessage('editProfileMessage', 'New password must be at least 4 characters long.', 'error'); return; } setButtonLoading('editProfileBtn', true); clearEditProfileMessages(); try { const requestBody = { currentPassword }; if (isPasswordChange) { requestBody.newPassword = newPassword; } if (isEmailChange) { requestBody.email = newEmail; } const response = await fetch(`${API_BASE_URL}/users/update-profile`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('authToken')}` }, body: JSON.stringify(requestBody) }); if (response.ok) { const data = await response.json(); let message = ''; if (isPasswordChange && isEmailChange) { message = 'Email and password updated successfully! Please log in with your new password.'; document.getElementById('editProfileForm').reset(); setTimeout(() => logout(), 1500); } else if (isPasswordChange) { message = 'Password updated successfully! Please log in with your new password.'; document.getElementById('editProfileForm').reset(); setTimeout(() => logout(), 1500); } else if (isEmailChange) { message = 'Email updated successfully!'; // Update the stored user data with new email currentUser.email = newEmail; localStorage.setItem('userData', JSON.stringify(currentUser)); updateUserDisplay(); // Refresh the displayed email document.getElementById('editProfileForm').reset(); setTimeout(() => hideEditProfileForm(), 1000); } showMessage('editProfileMessage', message, 'success'); } else { try { const errorData = await response.json(); const errorMessage = errorData.message || 'Failed to update profile. Please try again.'; showMessage('editProfileMessage', errorMessage, 'error'); } catch (parseError) { // If response is not JSON, fall back to text const errorText = await response.text(); showMessage('editProfileMessage', errorText || 'Failed to update profile. Please try again.', 'error'); } } } catch (error) { console.error('Update profile error:', error); showMessage('editProfileMessage', 'Network error. Please check if the auth service is running.', 'error'); } finally { setButtonLoading('editProfileBtn', false); } }); } function clearEditProfileMessages() { const messageElement = document.getElementById('editProfileMessage'); messageElement.style.display = 'none'; messageElement.textContent = ''; } function clearResetPasswordMessages() { const messageElement = document.getElementById('resetPasswordMessage'); messageElement.style.display = 'none'; messageElement.textContent = ''; } function updateUserDisplay() { if (!currentUser) { console.log('updateUserDisplay called but no currentUser'); return; } const usernameEl = document.getElementById('currentUsername'); const emailEl = document.getElementById('currentEmail'); const userIdEl = document.getElementById('currentUserId'); const rolesEl = document.getElementById('currentRoles'); if (usernameEl) usernameEl.textContent = currentUser.username || ''; if (emailEl) emailEl.textContent = maskUserEmail(currentUser.email) || '-'; if (userIdEl) userIdEl.textContent = maskUserId(currentUser.id) || ''; if (rolesEl) rolesEl.textContent = (currentUser.roles || []).join(', '); // Show/hide admin panel button based on user role updateAdminPanelVisibility(); } function showUserSection() { const authSection = document.getElementById('authSection'); const userSection = document.getElementById('userSection'); const header = document.querySelector('.header'); if (authSection) authSection.style.display = 'none'; if (userSection) { userSection.classList.add('active'); userSection.style.display = 'block'; } if (header) header.style.display = 'block'; updateUserDisplay(); } function showResetPasswordSection() { document.getElementById('authSection').style.display = 'none'; document.getElementById('userSection').classList.remove('active'); document.getElementById('resetPasswordSection').style.display = 'block'; document.querySelector('.header').style.display = 'none'; document.getElementById('resetNewPassword').focus(); } function maskUserId(id) { const parts = id.split("-"); return parts[0] + "-****-" + parts[4]; } function maskUserEmail(email) { if (!email.includes("@")) { return email; } const [user, domain] = email.split("@"); if (user.length <= 2) { return user[0] + "*@" + domain; } const first = user[0]; const last = user[user.length - 1]; const maskedPart = "*".repeat(user.length - 2); return `${first}${maskedPart}${last}@${domain}`; } function logout() { localStorage.removeItem('authToken'); localStorage.removeItem('userData'); currentUser = null; const userSection = document.getElementById('userSection'); const authSection = document.getElementById('authSection'); const header = document.querySelector('.header'); const loginForm = document.getElementById('loginForm'); const registerForm = document.getElementById('registerForm'); if (userSection) { userSection.classList.remove('active'); userSection.style.display = 'none'; } if (authSection) authSection.style.display = 'block'; if (header) header.style.display = 'block'; if (loginForm) loginForm.reset(); if (registerForm) registerForm.reset(); hideEditProfileForm(); clearMessages(); switchTab('login'); } function toggleEditProfileForm() { const formSection = document.getElementById('editProfileSection'); const isVisible = formSection.style.display === 'block'; if (isVisible) { hideEditProfileForm(); } else { showEditProfileForm(); } } function showEditProfileForm() { const formSection = document.getElementById('editProfileSection'); const actionBtn = document.getElementById('showEditProfileBtn'); if (formSection) formSection.style.display = 'block'; // Add active styling to button if (actionBtn) actionBtn.classList.add('active'); // Clear any previous messages clearEditProfileMessages(); // Pre-populate email field with current user's email if (currentUser) { const editEmail = document.getElementById('editEmail'); if (editEmail) editEmail.value = currentUser.email || ''; } // Focus on email input first since that's the most common change setTimeout(() => { const editEmail = document.getElementById('editEmail'); if (editEmail) editEmail.focus(); }, 100); } function hideEditProfileForm() { const formSection = document.getElementById('editProfileSection'); const actionBtn = document.getElementById('showEditProfileBtn'); if (formSection) formSection.style.display = 'none'; // Remove active styling from button if (actionBtn) actionBtn.classList.remove('active'); // Clear form and messages const editForm = document.getElementById('editProfileForm'); if (editForm) editForm.reset(); clearEditProfileMessages(); } // Admin panel functions function hasAdminRole() { return currentUser && Array.isArray(currentUser.roles) && currentUser.roles.includes('ADMIN'); } function updateAdminPanelVisibility() { const adminBtn = document.getElementById('adminPanelBtn'); if (adminBtn) { adminBtn.style.display = hasAdminRole() ? 'block' : 'none'; } } function openAdminPanel() { if (!hasAdminRole()) { alert('Access denied. Admin privileges required.'); return; } window.location.href = 'admin.html'; } async function handleForgotPassword(e) { e.preventDefault(); const username = document.getElementById('loginUsername').value.trim(); if (!username) { showMessage('loginMessage', 'Please enter your username above and then click "Forgot password" again.', 'error'); document.getElementById('loginUsername').focus(); return; } clearMessages(); const link = document.getElementById('forgotPasswordLink'); const originalText = link.textContent; link.textContent = 'Sending email...'; link.style.pointerEvents = 'none'; try { const response = await fetch(`${API_BASE_URL}/forgot-password?username=${encodeURIComponent(username)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); if (response.ok) { showMessage('loginMessage', 'Password reset email sent! Please check your inbox.', 'success'); document.getElementById('loginUsername').value = ''; } else { try { const errorData = await response.json(); const errorMessage = errorData.message || 'Failed to send reset email. Please try again.'; showMessage('loginMessage', errorMessage, 'error'); } catch (parseError) { const errorText = await response.text(); showMessage('loginMessage', errorText || 'Failed to send reset email. Please try again.', 'error'); } } } catch (error) { console.error('Forgot password error:', error); showMessage('loginMessage', 'Network error. Please check if the auth service is running.', 'error'); } finally { link.textContent = originalText; link.style.pointerEvents = 'auto'; } }