|
@@ -1,19 +1,15 @@
|
|
|
-// Admin Panel JavaScript
|
|
|
|
|
let currentUsers = [];
|
|
let currentUsers = [];
|
|
|
let filteredUsers = [];
|
|
let filteredUsers = [];
|
|
|
|
|
|
|
|
-// Check if user has admin role on page load
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
initializeTheme();
|
|
initializeTheme();
|
|
|
checkAdminAccess();
|
|
checkAdminAccess();
|
|
|
loadUsers();
|
|
loadUsers();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-// Theme toggle functionality (reused from main.js)
|
|
|
|
|
function toggleTheme() {
|
|
function toggleTheme() {
|
|
|
const currentTheme = document.documentElement.getAttribute('data-theme');
|
|
const currentTheme = document.documentElement.getAttribute('data-theme');
|
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
|
-
|
|
|
|
|
document.documentElement.setAttribute('data-theme', newTheme);
|
|
document.documentElement.setAttribute('data-theme', newTheme);
|
|
|
localStorage.setItem('theme', newTheme);
|
|
localStorage.setItem('theme', newTheme);
|
|
|
}
|
|
}
|
|
@@ -23,47 +19,36 @@ function initializeTheme() {
|
|
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Check admin access
|
|
|
|
|
function checkAdminAccess() {
|
|
function checkAdminAccess() {
|
|
|
const currentUser = getCurrentUser();
|
|
const currentUser = getCurrentUser();
|
|
|
-
|
|
|
|
|
if (!currentUser || !hasAdminRole(currentUser.roles)) {
|
|
if (!currentUser || !hasAdminRole(currentUser.roles)) {
|
|
|
showMessage('Access denied. Admin privileges required.', 'error');
|
|
showMessage('Access denied. Admin privileges required.', 'error');
|
|
|
- setTimeout(() => {
|
|
|
|
|
- window.location.href = 'index.html';
|
|
|
|
|
- }, 2000);
|
|
|
|
|
|
|
+ setTimeout(() => window.location.href = 'index.html', 2000);
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Get current user from localStorage
|
|
|
|
|
function getCurrentUser() {
|
|
function getCurrentUser() {
|
|
|
try {
|
|
try {
|
|
|
const userData = localStorage.getItem('userData');
|
|
const userData = localStorage.getItem('userData');
|
|
|
- if (!userData) return null;
|
|
|
|
|
- return JSON.parse(userData);
|
|
|
|
|
|
|
+ return userData ? JSON.parse(userData) : null;
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
- console.error('Error parsing user data:', e);
|
|
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Check if user has admin role
|
|
|
|
|
function hasAdminRole(roles) {
|
|
function hasAdminRole(roles) {
|
|
|
return Array.isArray(roles) && roles.includes('ADMIN');
|
|
return Array.isArray(roles) && roles.includes('ADMIN');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Load users from API
|
|
|
|
|
async function loadUsers() {
|
|
async function loadUsers() {
|
|
|
showLoading(true);
|
|
showLoading(true);
|
|
|
hideEmptyState();
|
|
hideEmptyState();
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
const token = localStorage.getItem('authToken');
|
|
const token = localStorage.getItem('authToken');
|
|
|
- if (!token) {
|
|
|
|
|
- throw new Error('No authentication token found');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!token) throw new Error('No authentication token found');
|
|
|
|
|
|
|
|
const response = await fetch(API_BASE_URL + '/users', {
|
|
const response = await fetch(API_BASE_URL + '/users', {
|
|
|
method: 'GET',
|
|
method: 'GET',
|
|
@@ -81,15 +66,13 @@ async function loadUsers() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const users = await response.json();
|
|
const users = await response.json();
|
|
|
-
|
|
|
|
|
- // Transform API response to match our display format
|
|
|
|
|
const transformedUsers = users.map(user => ({
|
|
const transformedUsers = users.map(user => ({
|
|
|
id: user.id,
|
|
id: user.id,
|
|
|
username: user.username,
|
|
username: user.username,
|
|
|
email: user.email,
|
|
email: user.email,
|
|
|
roles: user.roles,
|
|
roles: user.roles,
|
|
|
status: user.active ? 'active' : 'inactive',
|
|
status: user.active ? 'active' : 'inactive',
|
|
|
- lastLogin: null // Not provided by API
|
|
|
|
|
|
|
+ lastLogin: null
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
currentUsers = transformedUsers;
|
|
currentUsers = transformedUsers;
|
|
@@ -100,11 +83,9 @@ async function loadUsers() {
|
|
|
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('Error loading users:', error);
|
|
console.error('Error loading users:', error);
|
|
|
- if (error.message.includes('Access denied') || error.message.includes('Admin privileges')) {
|
|
|
|
|
|
|
+ if (error.message.includes('Access denied')) {
|
|
|
showMessage('Access denied. Admin privileges required.', 'error');
|
|
showMessage('Access denied. Admin privileges required.', 'error');
|
|
|
- setTimeout(() => {
|
|
|
|
|
- window.location.href = 'index.html';
|
|
|
|
|
- }, 2000);
|
|
|
|
|
|
|
+ setTimeout(() => window.location.href = 'index.html', 2000);
|
|
|
} else {
|
|
} else {
|
|
|
showMessage('Failed to load users. Please try again.', 'error');
|
|
showMessage('Failed to load users. Please try again.', 'error');
|
|
|
}
|
|
}
|
|
@@ -114,7 +95,6 @@ async function loadUsers() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Display users in table
|
|
|
|
|
function displayUsers(users) {
|
|
function displayUsers(users) {
|
|
|
const tableBody = document.getElementById('usersTableBody');
|
|
const tableBody = document.getElementById('usersTableBody');
|
|
|
|
|
|
|
@@ -146,23 +126,14 @@ function displayUsers(users) {
|
|
|
</td>
|
|
</td>
|
|
|
<td>
|
|
<td>
|
|
|
<div class="action-buttons">
|
|
<div class="action-buttons">
|
|
|
- <button class="action-btn small" onclick="viewUser('${user.id}')" title="View Details">
|
|
|
|
|
- <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
- <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
|
|
|
|
|
- </svg>
|
|
|
|
|
- </button>
|
|
|
|
|
- <button class="action-btn small secondary" onclick="editUser('${user.id}')" title="Edit User">
|
|
|
|
|
- <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor">
|
|
|
|
|
- <path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/>
|
|
|
|
|
- </svg>
|
|
|
|
|
- </button>
|
|
|
|
|
|
|
+ <button class="action-btn small" onclick="viewUser('${user.id}')" title="View Details">👁️</button>
|
|
|
|
|
+ <button class="action-btn small secondary" onclick="editUser('${user.id}')" title="Edit User">✏️</button>
|
|
|
</div>
|
|
</div>
|
|
|
</td>
|
|
</td>
|
|
|
</tr>
|
|
</tr>
|
|
|
`).join('');
|
|
`).join('');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Update statistics
|
|
|
|
|
function updateStats(users) {
|
|
function updateStats(users) {
|
|
|
const totalUsers = users.length;
|
|
const totalUsers = users.length;
|
|
|
const activeUsers = users.filter(u => u.status === 'active').length;
|
|
const activeUsers = users.filter(u => u.status === 'active').length;
|
|
@@ -173,10 +144,8 @@ function updateStats(users) {
|
|
|
document.getElementById('adminUsers').textContent = adminUsers;
|
|
document.getElementById('adminUsers').textContent = adminUsers;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Filter users based on search input
|
|
|
|
|
function filterUsers() {
|
|
function filterUsers() {
|
|
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
|
-
|
|
|
|
|
filteredUsers = currentUsers.filter(user =>
|
|
filteredUsers = currentUsers.filter(user =>
|
|
|
user.username.toLowerCase().includes(searchTerm) ||
|
|
user.username.toLowerCase().includes(searchTerm) ||
|
|
|
user.id.toLowerCase().includes(searchTerm) ||
|
|
user.id.toLowerCase().includes(searchTerm) ||
|
|
@@ -184,11 +153,9 @@ function filterUsers() {
|
|
|
user.roles.some(role => role.toLowerCase().includes(searchTerm)) ||
|
|
user.roles.some(role => role.toLowerCase().includes(searchTerm)) ||
|
|
|
user.status.toLowerCase().includes(searchTerm)
|
|
user.status.toLowerCase().includes(searchTerm)
|
|
|
);
|
|
);
|
|
|
-
|
|
|
|
|
displayUsers(filteredUsers);
|
|
displayUsers(filteredUsers);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// User action functions
|
|
|
|
|
function viewUser(userId) {
|
|
function viewUser(userId) {
|
|
|
const user = currentUsers.find(u => u.id === userId);
|
|
const user = currentUsers.find(u => u.id === userId);
|
|
|
if (user) {
|
|
if (user) {
|
|
@@ -206,53 +173,35 @@ function viewUser(userId) {
|
|
|
|
|
|
|
|
function editUser(userId) {
|
|
function editUser(userId) {
|
|
|
const user = currentUsers.find(u => u.id === userId);
|
|
const user = currentUsers.find(u => u.id === userId);
|
|
|
- if (user) {
|
|
|
|
|
- openEditUserModal(user);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (user) openEditUserModal(user);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Edit User Modal Functions
|
|
|
|
|
let currentEditUserId = null;
|
|
let currentEditUserId = null;
|
|
|
|
|
|
|
|
function openEditUserModal(user) {
|
|
function openEditUserModal(user) {
|
|
|
currentEditUserId = user.id;
|
|
currentEditUserId = user.id;
|
|
|
const modal = document.getElementById('editUserModal');
|
|
const modal = document.getElementById('editUserModal');
|
|
|
|
|
|
|
|
- // Populate form with user data
|
|
|
|
|
document.getElementById('editUserEmail').value = user.email || '';
|
|
document.getElementById('editUserEmail').value = user.email || '';
|
|
|
document.getElementById('editUserActive').checked = user.status === 'active';
|
|
document.getElementById('editUserActive').checked = user.status === 'active';
|
|
|
|
|
|
|
|
- // Clear all role checkboxes first
|
|
|
|
|
document.querySelectorAll('input[name="role"]').forEach(checkbox => {
|
|
document.querySelectorAll('input[name="role"]').forEach(checkbox => {
|
|
|
checkbox.checked = false;
|
|
checkbox.checked = false;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // Check the roles the user has
|
|
|
|
|
user.roles.forEach(role => {
|
|
user.roles.forEach(role => {
|
|
|
const checkbox = document.querySelector(`input[name="role"][value="${role}"]`);
|
|
const checkbox = document.querySelector(`input[name="role"][value="${role}"]`);
|
|
|
- if (checkbox) {
|
|
|
|
|
- checkbox.checked = true;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (checkbox) checkbox.checked = true;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // Clear any previous messages
|
|
|
|
|
clearEditUserMessage();
|
|
clearEditUserMessage();
|
|
|
-
|
|
|
|
|
- // Show modal
|
|
|
|
|
modal.style.display = 'flex';
|
|
modal.style.display = 'flex';
|
|
|
-
|
|
|
|
|
- // Focus on email input
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- document.getElementById('editUserEmail').focus();
|
|
|
|
|
- }, 100);
|
|
|
|
|
|
|
+ setTimeout(() => document.getElementById('editUserEmail').focus(), 100);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function closeEditUserModal() {
|
|
function closeEditUserModal() {
|
|
|
- const modal = document.getElementById('editUserModal');
|
|
|
|
|
- modal.style.display = 'none';
|
|
|
|
|
|
|
+ document.getElementById('editUserModal').style.display = 'none';
|
|
|
currentEditUserId = null;
|
|
currentEditUserId = null;
|
|
|
-
|
|
|
|
|
- // Reset form
|
|
|
|
|
document.getElementById('editUserForm').reset();
|
|
document.getElementById('editUserForm').reset();
|
|
|
clearEditUserMessage();
|
|
clearEditUserMessage();
|
|
|
}
|
|
}
|
|
@@ -283,13 +232,11 @@ async function handleEditUserSubmit(event) {
|
|
|
const email = formData.get('email').trim();
|
|
const email = formData.get('email').trim();
|
|
|
const isActive = formData.has('isActive');
|
|
const isActive = formData.has('isActive');
|
|
|
|
|
|
|
|
- // Get selected roles
|
|
|
|
|
const selectedRoles = [];
|
|
const selectedRoles = [];
|
|
|
document.querySelectorAll('input[name="role"]:checked').forEach(checkbox => {
|
|
document.querySelectorAll('input[name="role"]:checked').forEach(checkbox => {
|
|
|
selectedRoles.push(checkbox.value);
|
|
selectedRoles.push(checkbox.value);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // Validation
|
|
|
|
|
if (selectedRoles.length === 0) {
|
|
if (selectedRoles.length === 0) {
|
|
|
showEditUserMessage('Please select at least one role', 'error');
|
|
showEditUserMessage('Please select at least one role', 'error');
|
|
|
return;
|
|
return;
|
|
@@ -300,14 +247,12 @@ async function handleEditUserSubmit(event) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Prepare request data
|
|
|
|
|
const requestData = {
|
|
const requestData = {
|
|
|
roles: selectedRoles,
|
|
roles: selectedRoles,
|
|
|
email: email || null,
|
|
email: email || null,
|
|
|
isActive: isActive
|
|
isActive: isActive
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- // Update UI
|
|
|
|
|
const saveBtn = document.getElementById('saveUserBtn');
|
|
const saveBtn = document.getElementById('saveUserBtn');
|
|
|
const originalText = saveBtn.textContent;
|
|
const originalText = saveBtn.textContent;
|
|
|
saveBtn.textContent = 'Saving...';
|
|
saveBtn.textContent = 'Saving...';
|
|
@@ -316,9 +261,7 @@ async function handleEditUserSubmit(event) {
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
const token = localStorage.getItem('authToken');
|
|
const token = localStorage.getItem('authToken');
|
|
|
- if (!token) {
|
|
|
|
|
- throw new Error('No authentication token found');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (!token) throw new Error('No authentication token found');
|
|
|
|
|
|
|
|
const response = await fetch(`${API_BASE_URL}/users/${currentEditUserId}`, {
|
|
const response = await fetch(`${API_BASE_URL}/users/${currentEditUserId}`, {
|
|
|
method: 'PUT',
|
|
method: 'PUT',
|
|
@@ -345,8 +288,6 @@ async function handleEditUserSubmit(event) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
showEditUserMessage('User updated successfully!', 'success');
|
|
showEditUserMessage('User updated successfully!', 'success');
|
|
|
-
|
|
|
|
|
- // Refresh the users list to show updated data
|
|
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
loadUsers();
|
|
loadUsers();
|
|
|
closeEditUserModal();
|
|
closeEditUserModal();
|
|
@@ -362,31 +303,10 @@ async function handleEditUserSubmit(event) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function isValidEmail(email) {
|
|
function isValidEmail(email) {
|
|
|
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
|
|
|
- return emailRegex.test(email);
|
|
|
|
|
|
|
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Close modal when clicking outside of it
|
|
|
|
|
-document.addEventListener('click', function(event) {
|
|
|
|
|
- const modal = document.getElementById('editUserModal');
|
|
|
|
|
- if (event.target === modal) {
|
|
|
|
|
- closeEditUserModal();
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// Close modal with Escape key
|
|
|
|
|
-document.addEventListener('keydown', function(event) {
|
|
|
|
|
- if (event.key === 'Escape') {
|
|
|
|
|
- const modal = document.getElementById('editUserModal');
|
|
|
|
|
- if (modal.style.display === 'flex') {
|
|
|
|
|
- closeEditUserModal();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// Utility functions
|
|
|
|
|
function formatUserId(id) {
|
|
function formatUserId(id) {
|
|
|
- // Format UUID for display (show first 8 characters)
|
|
|
|
|
return id.substring(0, 8) + '...';
|
|
return id.substring(0, 8) + '...';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -397,8 +317,7 @@ function formatDate(dateString) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function showLoading(show) {
|
|
function showLoading(show) {
|
|
|
- const indicator = document.getElementById('loadingIndicator');
|
|
|
|
|
- indicator.style.display = show ? 'block' : 'none';
|
|
|
|
|
|
|
+ document.getElementById('loadingIndicator').style.display = show ? 'block' : 'none';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function showEmptyState() {
|
|
function showEmptyState() {
|
|
@@ -415,19 +334,19 @@ function showMessage(message, type = 'info') {
|
|
|
messageContainer.className = `message ${type}`;
|
|
messageContainer.className = `message ${type}`;
|
|
|
messageContainer.style.display = 'block';
|
|
messageContainer.style.display = 'block';
|
|
|
|
|
|
|
|
- // Auto-hide message after 3 seconds for success messages
|
|
|
|
|
if (type === 'success') {
|
|
if (type === 'success') {
|
|
|
- setTimeout(() => {
|
|
|
|
|
- messageContainer.style.display = 'none';
|
|
|
|
|
- }, 3000);
|
|
|
|
|
|
|
+ setTimeout(() => messageContainer.style.display = 'none', 3000);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Export functions for potential use in other scripts
|
|
|
|
|
-window.adminAPI = {
|
|
|
|
|
- loadUsers,
|
|
|
|
|
- filterUsers,
|
|
|
|
|
- viewUser,
|
|
|
|
|
- editUser,
|
|
|
|
|
- toggleTheme
|
|
|
|
|
-};
|
|
|
|
|
|
|
+document.addEventListener('click', function(event) {
|
|
|
|
|
+ const modal = document.getElementById('editUserModal');
|
|
|
|
|
+ if (event.target === modal) closeEditUserModal();
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+document.addEventListener('keydown', function(event) {
|
|
|
|
|
+ if (event.key === 'Escape') {
|
|
|
|
|
+ const modal = document.getElementById('editUserModal');
|
|
|
|
|
+ if (modal.style.display === 'flex') closeEditUserModal();
|
|
|
|
|
+ }
|
|
|
|
|
+});
|