|
@@ -256,6 +256,21 @@
|
|
|
@keyframes spin {
|
|
@keyframes spin {
|
|
|
to { transform: rotate(360deg); }
|
|
to { transform: rotate(360deg); }
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ .profile-section, .security-section {
|
|
|
|
|
+ margin-bottom: 25px;
|
|
|
|
|
+ padding: 15px;
|
|
|
|
|
+ background: #f8f9fa;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ border-left: 3px solid #667eea;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .profile-section h4, .security-section h4 {
|
|
|
|
|
+ margin: 0 0 15px 0;
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ font-size: 1rem;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|
|
|
</head>
|
|
</head>
|
|
|
<body>
|
|
<body>
|
|
@@ -289,6 +304,10 @@
|
|
|
<label for="registerUsername">Username</label>
|
|
<label for="registerUsername">Username</label>
|
|
|
<input type="text" id="registerUsername" name="username" required>
|
|
<input type="text" id="registerUsername" name="username" required>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="form-group">
|
|
|
|
|
+ <label for="registerEmail">Email (optional)</label>
|
|
|
|
|
+ <input type="text" id="registerEmail" name="email">
|
|
|
|
|
+ </div>
|
|
|
<div class="form-group">
|
|
<div class="form-group">
|
|
|
<label for="registerPassword">Password</label>
|
|
<label for="registerPassword">Password</label>
|
|
|
<input type="password" id="registerPassword" name="password" required>
|
|
<input type="password" id="registerPassword" name="password" required>
|
|
@@ -324,34 +343,53 @@
|
|
|
<div id="userSection" class="user-info">
|
|
<div id="userSection" class="user-info">
|
|
|
<div class="user-details">
|
|
<div class="user-details">
|
|
|
<p><strong>Username:</strong> <span id="currentUsername"></span></p>
|
|
<p><strong>Username:</strong> <span id="currentUsername"></span></p>
|
|
|
|
|
+ <p><strong>Email:</strong> <span id="currentEmail"></span></p>
|
|
|
<p><strong>User ID:</strong> <span id="currentUserId"></span></p>
|
|
<p><strong>User ID:</strong> <span id="currentUserId"></span></p>
|
|
|
<p><strong>Roles:</strong> <span id="currentRoles"></span></p>
|
|
<p><strong>Roles:</strong> <span id="currentRoles"></span></p>
|
|
|
<p><strong>Token Expires:</strong> <span id="tokenExpiration"></span></p>
|
|
<p><strong>Token Expires:</strong> <span id="tokenExpiration"></span></p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="action-buttons">
|
|
<div class="action-buttons">
|
|
|
- <button class="action-btn" id="showChangePasswordBtn" onclick="toggleChangePasswordForm()">Change Password</button>
|
|
|
|
|
|
|
+ <button class="action-btn" id="showEditProfileBtn" onclick="toggleEditProfileForm()">Edit Profile</button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div class="change-password-section" id="changePasswordSection" style="display: none;">
|
|
|
|
|
- <h3>Change Password</h3>
|
|
|
|
|
- <form id="changePasswordForm">
|
|
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label for="currentPassword">Current Password</label>
|
|
|
|
|
- <input type="password" id="currentPassword" name="currentPassword" required>
|
|
|
|
|
|
|
+ <div class="change-password-section" id="editProfileSection" style="display: none;">
|
|
|
|
|
+ <h3>Edit Profile</h3>
|
|
|
|
|
+<!-- <div style="background: #e8f4f8; padding: 12px; border-radius: 6px; margin-bottom: 20px; font-size: 0.9rem; color: #2c5282;">-->
|
|
|
|
|
+<!-- <strong>How it works:</strong> You can update your email address, change your password, or both. Your current password is required for security verification.-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
+ <form id="editProfileForm">
|
|
|
|
|
+ <div class="profile-section">
|
|
|
|
|
+ <h4 style="margin-bottom: 15px; color: #333; font-size: 1rem;">Email Address</h4>
|
|
|
|
|
+ <div class="form-group">
|
|
|
|
|
+ <label for="editEmail">Email</label>
|
|
|
|
|
+ <input type="email" id="editEmail" name="email" placeholder="Enter your email address">
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label for="newPassword">New Password</label>
|
|
|
|
|
- <input type="password" id="newPassword" name="newPassword" required>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <div class="profile-section">
|
|
|
|
|
+ <h4 style="margin-bottom: 15px; color: #333; font-size: 1rem;">Change Password</h4>
|
|
|
|
|
+ <div class="form-group">
|
|
|
|
|
+ <label for="newPassword">New Password</label>
|
|
|
|
|
+ <input type="password" id="newPassword" name="newPassword" placeholder="Leave empty to keep current password">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="form-group">
|
|
|
|
|
+ <label for="confirmPassword">Confirm New Password</label>
|
|
|
|
|
+ <input type="password" id="confirmPassword" name="confirmPassword" placeholder="Confirm your new password">
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="form-group">
|
|
|
|
|
- <label for="confirmPassword">Confirm New Password</label>
|
|
|
|
|
- <input type="password" id="confirmPassword" name="confirmPassword" required>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <div class="security-section">
|
|
|
|
|
+ <h4 style="margin-bottom: 15px; color: #333; font-size: 1rem;">Security Verification</h4>
|
|
|
|
|
+ <div class="form-group">
|
|
|
|
|
+ <label for="currentPassword">Current Password</label>
|
|
|
|
|
+ <input type="password" id="currentPassword" name="currentPassword" required placeholder="Enter current password to verify changes">
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="form-buttons">
|
|
<div class="form-buttons">
|
|
|
- <button type="submit" class="submit-btn" id="changePasswordBtn">Change Password</button>
|
|
|
|
|
|
|
+ <button type="submit" class="submit-btn" id="editProfileBtn">Update Profile</button>
|
|
|
</div>
|
|
</div>
|
|
|
- <div id="changePasswordMessage" class="message" style="display: none;"></div>
|
|
|
|
|
|
|
+ <div id="editProfileMessage" class="message" style="display: none;"></div>
|
|
|
</form>
|
|
</form>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -433,8 +471,8 @@
|
|
|
button.innerHTML = 'Sign In';
|
|
button.innerHTML = 'Sign In';
|
|
|
} else if (buttonId.includes('register')) {
|
|
} else if (buttonId.includes('register')) {
|
|
|
button.innerHTML = 'Create Account';
|
|
button.innerHTML = 'Create Account';
|
|
|
- } else if (buttonId.includes('changePassword')) {
|
|
|
|
|
- button.innerHTML = 'Change Password';
|
|
|
|
|
|
|
+ } else if (buttonId.includes('editProfile')) {
|
|
|
|
|
+ button.innerHTML = 'Update Profile';
|
|
|
} else if (buttonId.includes('resetPassword')) {
|
|
} else if (buttonId.includes('resetPassword')) {
|
|
|
button.innerHTML = 'Reset Password';
|
|
button.innerHTML = 'Reset Password';
|
|
|
}
|
|
}
|
|
@@ -486,6 +524,7 @@
|
|
|
|
|
|
|
|
const username = document.getElementById('registerUsername').value;
|
|
const username = document.getElementById('registerUsername').value;
|
|
|
const password = document.getElementById('registerPassword').value;
|
|
const password = document.getElementById('registerPassword').value;
|
|
|
|
|
+ const email = document.getElementById('registerEmail').value;
|
|
|
|
|
|
|
|
setButtonLoading('registerBtn', true);
|
|
setButtonLoading('registerBtn', true);
|
|
|
clearMessages();
|
|
clearMessages();
|
|
@@ -496,7 +535,7 @@
|
|
|
headers: {
|
|
headers: {
|
|
|
'Content-Type': 'application/json'
|
|
'Content-Type': 'application/json'
|
|
|
},
|
|
},
|
|
|
- body: JSON.stringify({ username, password })
|
|
|
|
|
|
|
+ body: JSON.stringify({ username, email, password })
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
if (response.ok) {
|
|
@@ -526,15 +565,13 @@
|
|
|
const newPassword = document.getElementById('resetNewPassword').value;
|
|
const newPassword = document.getElementById('resetNewPassword').value;
|
|
|
const confirmPassword = document.getElementById('resetConfirmPassword').value;
|
|
const confirmPassword = document.getElementById('resetConfirmPassword').value;
|
|
|
|
|
|
|
|
- // Validate password confirmation
|
|
|
|
|
if (newPassword !== confirmPassword) {
|
|
if (newPassword !== confirmPassword) {
|
|
|
showMessage('resetPasswordMessage', 'New passwords do not match.', 'error');
|
|
showMessage('resetPasswordMessage', 'New passwords do not match.', 'error');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Validate password length
|
|
|
|
|
if (newPassword.length < 4) {
|
|
if (newPassword.length < 4) {
|
|
|
- showMessage('resetPasswordMessage', 'New password must be at least 6 characters long.', 'error');
|
|
|
|
|
|
|
+ showMessage('resetPasswordMessage', 'New password must be at least 4 characters long.', 'error');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -572,60 +609,95 @@
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- document.getElementById('changePasswordForm').addEventListener('submit', async function(e) {
|
|
|
|
|
|
|
+ document.getElementById('editProfileForm').addEventListener('submit', async function(e) {
|
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
const currentPassword = document.getElementById('currentPassword').value;
|
|
const currentPassword = document.getElementById('currentPassword').value;
|
|
|
const newPassword = document.getElementById('newPassword').value;
|
|
const newPassword = document.getElementById('newPassword').value;
|
|
|
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
|
|
|
+ const email = document.getElementById('editEmail').value;
|
|
|
|
|
|
|
|
- // Validate password confirmation
|
|
|
|
|
- if (newPassword !== confirmPassword) {
|
|
|
|
|
- showMessage('changePasswordMessage', 'New passwords do not match.', 'error');
|
|
|
|
|
|
|
+ // 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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Validate password length
|
|
|
|
|
- if (newPassword.length < 6) {
|
|
|
|
|
- showMessage('changePasswordMessage', 'New password must be at least 6 characters long.', 'error');
|
|
|
|
|
|
|
+ if (isPasswordChange && newPassword !== confirmPassword) {
|
|
|
|
|
+ showMessage('editProfileMessage', 'New passwords do not match.', 'error');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- setButtonLoading('changePasswordBtn', true);
|
|
|
|
|
- clearPasswordChangeMessages();
|
|
|
|
|
|
|
+ if (isPasswordChange && newPassword.length < 4) {
|
|
|
|
|
+ showMessage('editProfileMessage', 'New password must be at least 4 characters long.', 'error');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ setButtonLoading('editProfileBtn', true);
|
|
|
|
|
+ clearEditProfileMessages();
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- const response = await fetch(`${API_BASE_URL}/users/change-password`, {
|
|
|
|
|
|
|
+ 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',
|
|
method: 'POST',
|
|
|
headers: {
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
'Content-Type': 'application/json',
|
|
|
'Authorization': `Bearer ${localStorage.getItem('authToken')}`
|
|
'Authorization': `Bearer ${localStorage.getItem('authToken')}`
|
|
|
},
|
|
},
|
|
|
- body: JSON.stringify({ currentPassword, newPassword })
|
|
|
|
|
|
|
+ body: JSON.stringify(requestBody)
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
if (response.ok) {
|
|
if (response.ok) {
|
|
|
const data = await response.json();
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
- showMessage('changePasswordMessage', 'Password changed successfully! Please log in with your new password.', 'success');
|
|
|
|
|
-
|
|
|
|
|
- // Clear the form and force logout after a brief delay
|
|
|
|
|
- document.getElementById('changePasswordForm').reset();
|
|
|
|
|
- logout();
|
|
|
|
|
|
|
+ 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 {
|
|
} else {
|
|
|
const errorText = await response.text();
|
|
const errorText = await response.text();
|
|
|
- showMessage('changePasswordMessage', errorText || 'Failed to change password. Please try again.', 'error');
|
|
|
|
|
|
|
+ showMessage('editProfileMessage', errorText || 'Failed to update profile. Please try again.', 'error');
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('Change password error:', error);
|
|
|
|
|
- showMessage('changePasswordMessage', 'Network error. Please check if the auth service is running.', 'error');
|
|
|
|
|
|
|
+ console.error('Update profile error:', error);
|
|
|
|
|
+ showMessage('editProfileMessage', 'Network error. Please check if the auth service is running.', 'error');
|
|
|
} finally {
|
|
} finally {
|
|
|
- setButtonLoading('changePasswordBtn', false);
|
|
|
|
|
|
|
+ setButtonLoading('editProfileBtn', false);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- function clearPasswordChangeMessages() {
|
|
|
|
|
- const messageElement = document.getElementById('changePasswordMessage');
|
|
|
|
|
|
|
+ function clearEditProfileMessages() {
|
|
|
|
|
+ const messageElement = document.getElementById('editProfileMessage');
|
|
|
messageElement.style.display = 'none';
|
|
messageElement.style.display = 'none';
|
|
|
messageElement.textContent = '';
|
|
messageElement.textContent = '';
|
|
|
}
|
|
}
|
|
@@ -638,6 +710,7 @@
|
|
|
|
|
|
|
|
function updateUserDisplay() {
|
|
function updateUserDisplay() {
|
|
|
document.getElementById('currentUsername').textContent = currentUser.username;
|
|
document.getElementById('currentUsername').textContent = currentUser.username;
|
|
|
|
|
+ document.getElementById('currentEmail').textContent = currentUser.email || 'Not set';
|
|
|
document.getElementById('currentUserId').textContent = maskUserId(currentUser.id);
|
|
document.getElementById('currentUserId').textContent = maskUserId(currentUser.id);
|
|
|
document.getElementById('currentRoles').textContent = currentUser.roles.join(', ');
|
|
document.getElementById('currentRoles').textContent = currentUser.roles.join(', ');
|
|
|
|
|
|
|
@@ -676,49 +749,54 @@
|
|
|
document.getElementById('loginForm').reset();
|
|
document.getElementById('loginForm').reset();
|
|
|
document.getElementById('registerForm').reset();
|
|
document.getElementById('registerForm').reset();
|
|
|
|
|
|
|
|
- // Reset password change form and hide it
|
|
|
|
|
- hideChangePasswordForm();
|
|
|
|
|
|
|
+ // Reset edit profile form and hide it
|
|
|
|
|
+ hideEditProfileForm();
|
|
|
|
|
|
|
|
clearMessages();
|
|
clearMessages();
|
|
|
|
|
|
|
|
switchTab('login');
|
|
switchTab('login');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function toggleChangePasswordForm() {
|
|
|
|
|
- const formSection = document.getElementById('changePasswordSection');
|
|
|
|
|
|
|
+ function toggleEditProfileForm() {
|
|
|
|
|
+ const formSection = document.getElementById('editProfileSection');
|
|
|
const isVisible = formSection.style.display === 'block';
|
|
const isVisible = formSection.style.display === 'block';
|
|
|
|
|
|
|
|
if (isVisible) {
|
|
if (isVisible) {
|
|
|
- hideChangePasswordForm();
|
|
|
|
|
|
|
+ hideEditProfileForm();
|
|
|
} else {
|
|
} else {
|
|
|
- showChangePasswordForm();
|
|
|
|
|
|
|
+ showEditProfileForm();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function showChangePasswordForm() {
|
|
|
|
|
- const formSection = document.getElementById('changePasswordSection');
|
|
|
|
|
- const actionBtn = document.getElementById('showChangePasswordBtn');
|
|
|
|
|
|
|
+ function showEditProfileForm() {
|
|
|
|
|
+ const formSection = document.getElementById('editProfileSection');
|
|
|
|
|
+ const actionBtn = document.getElementById('showEditProfileBtn');
|
|
|
|
|
|
|
|
formSection.style.display = 'block';
|
|
formSection.style.display = 'block';
|
|
|
- actionBtn.textContent = 'Hide Change Password';
|
|
|
|
|
|
|
+ actionBtn.textContent = 'Hide Edit Profile';
|
|
|
|
|
|
|
|
// Clear any previous messages
|
|
// Clear any previous messages
|
|
|
- clearPasswordChangeMessages();
|
|
|
|
|
|
|
+ clearEditProfileMessages();
|
|
|
|
|
+
|
|
|
|
|
+ // Pre-populate email field with current user's email
|
|
|
|
|
+ if (currentUser) {
|
|
|
|
|
+ document.getElementById('editEmail').value = currentUser.email || '';
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // Focus on first input
|
|
|
|
|
- document.getElementById('currentPassword').focus();
|
|
|
|
|
|
|
+ // Focus on email input first since that's the most common change
|
|
|
|
|
+ setTimeout(() => document.getElementById('editEmail').focus(), 100);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- function hideChangePasswordForm() {
|
|
|
|
|
- const formSection = document.getElementById('changePasswordSection');
|
|
|
|
|
- const actionBtn = document.getElementById('showChangePasswordBtn');
|
|
|
|
|
|
|
+ function hideEditProfileForm() {
|
|
|
|
|
+ const formSection = document.getElementById('editProfileSection');
|
|
|
|
|
+ const actionBtn = document.getElementById('showEditProfileBtn');
|
|
|
|
|
|
|
|
formSection.style.display = 'none';
|
|
formSection.style.display = 'none';
|
|
|
- actionBtn.textContent = 'Change Password';
|
|
|
|
|
|
|
+ actionBtn.textContent = 'Edit Profile';
|
|
|
|
|
|
|
|
// Clear form and messages
|
|
// Clear form and messages
|
|
|
- document.getElementById('changePasswordForm').reset();
|
|
|
|
|
- clearPasswordChangeMessages();
|
|
|
|
|
|
|
+ document.getElementById('editProfileForm').reset();
|
|
|
|
|
+ clearEditProfileMessages();
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
</body>
|
|
</body>
|