rewrite
This commit is contained in:
156
static/js/user_manager.js
Normal file
156
static/js/user_manager.js
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* 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 = '<tr><td colspan="5" class="text-center text-muted">No users found</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = users.map(user => `
|
||||
<tr>
|
||||
<td><strong>${escapeHtml(user.username)}</strong></td>
|
||||
<td>
|
||||
${user.is_admin ? '<span class="badge bg-danger">Admin</span>' : '<span class="badge bg-secondary">User</span>'}
|
||||
</td>
|
||||
<td><small class="text-muted">${formatDate(user.created_at)}</small></td>
|
||||
<td><small class="text-muted">${user.last_login ? formatDate(user.last_login) : 'Never'}</small></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary" onclick="editUser(${user.id}, '${escapeHtml(user.username)}', ${user.is_admin})">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
${user.id !== getUserId() ? `
|
||||
<button class="btn btn-sm btn-danger" onclick="deleteUser(${user.id}, '${escapeHtml(user.username)}')">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`).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}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user