/** * User Management UI */ let currentEditUserId = null; document.addEventListener('DOMContentLoaded', function() { loadUsers(); document.getElementById('createUserBtn').addEventListener('click', createUser); document.getElementById('updateUserBtn').addEventListener('click', updateUser); }); function loadUsers() { fetch('/api/users') .then(r => r.json()) .then(data => { if (data.success) { renderUsers(data.users); } }) .catch(e => console.error('Error loading users:', e)); } function renderUsers(users) { const tbody = document.getElementById('usersList'); if (users.length === 0) { tbody.innerHTML = 'No users found'; return; } tbody.innerHTML = users.map(user => ` ${escapeHtml(user.username)} ${user.is_admin ? 'Admin' : 'User'} ${formatDate(user.created_at)} ${user.last_login ? formatDate(user.last_login) : 'Never'} ${user.id !== getUserId() ? ` ` : ''} `).join(''); } function createUser() { const username = document.getElementById('newUsername').value; const password = document.getElementById('newPassword').value; const isAdmin = document.getElementById('newIsAdmin').checked; fetch('/api/users', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ username, password, is_admin: isAdmin }) }) .then(r => r.json()) .then(data => { if (data.success) { bootstrap.Modal.getInstance(document.getElementById('newUserModal')).hide(); document.getElementById('newUserForm').reset(); loadUsers(); showAlert('User created successfully', 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function editUser(userId, username, isAdmin) { currentEditUserId = userId; document.getElementById('editUsername').textContent = username; document.getElementById('editIsAdmin').checked = isAdmin; new bootstrap.Modal(document.getElementById('editUserModal')).show(); } function updateUser() { const password = document.getElementById('editPassword').value; const isAdmin = document.getElementById('editIsAdmin').checked; const body = { is_admin: isAdmin }; if (password) body.password = password; fetch(`/api/users/${currentEditUserId}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(body) }) .then(r => r.json()) .then(data => { if (data.success) { bootstrap.Modal.getInstance(document.getElementById('editUserModal')).hide(); loadUsers(); showAlert('User updated successfully', 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function deleteUser(userId, username) { if (!confirm(`Delete user '${username}'?`)) return; fetch(`/api/users/${userId}`, {method: 'DELETE'}) .then(r => r.json()) .then(data => { if (data.success) { loadUsers(); showAlert('User deleted successfully', 'success'); } else { showAlert(data.error, 'danger'); } }) .catch(e => showAlert(e.message, 'danger')); } function showAlert(message, type) { const alert = document.createElement('div'); alert.className = `alert alert-${type} alert-dismissible fade show`; alert.innerHTML = ` ${message} `; document.querySelector('.card-body').prepend(alert); setTimeout(() => alert.remove(), 5000); } function formatDate(dateStr) { return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } function escapeHtml(text) { const map = {'&': '&', '<': '<', '>': '>', '"': '"', "'": '''}; return text.replace(/[&<>"']/g, m => map[m]); } function getUserId() { // Extract from current user endpoint let userId = null; fetch('/api/current-user') .then(r => r.json()) .then(data => { userId = data.id; }); return userId; }