306 lines
10 KiB
Plaintext
306 lines
10 KiB
Plaintext
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<title data-text="title">Asteroid Radio - User Management</title>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<link rel="stylesheet" type="text/css" href="/asteroid/static/asteroid.css">
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1>👥 USER MANAGEMENT</h1>
|
||
<div class="nav">
|
||
<a href="/asteroid/admin">← Back to Admin</a>
|
||
<a href="/asteroid/">Home</a>
|
||
</div>
|
||
|
||
<!-- User Statistics -->
|
||
<div class="admin-section">
|
||
<h2>User Statistics</h2>
|
||
<div class="user-stats" id="user-stats">
|
||
<div class="stat-card">
|
||
<span class="stat-number" id="total-users">0</span>
|
||
<span class="stat-label">Total Users</span>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-number" id="active-users">0</span>
|
||
<span class="stat-label">Active Users</span>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-number" id="admin-users">0</span>
|
||
<span class="stat-label">Admins</span>
|
||
</div>
|
||
<div class="stat-card">
|
||
<span class="stat-number" id="dj-users">0</span>
|
||
<span class="stat-label">DJs</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- User Management Actions -->
|
||
<div class="admin-section">
|
||
<h2>User Actions</h2>
|
||
<div class="controls">
|
||
<button class="btn btn-primary" onclick="loadUsers()">👥 View All Users</button>
|
||
<button class="btn btn-success" onclick="toggleCreateUserForm()">➕ Create New User</button>
|
||
<button class="btn btn-secondary" onclick="refreshStats()">🔄 Refresh Stats</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Create User Form (hidden by default) -->
|
||
<div class="admin-section" id="create-user-form" style="display: none;">
|
||
<h2>Create New User</h2>
|
||
<form onsubmit="createNewUser(event)">
|
||
<div class="form-group">
|
||
<label>Username:</label>
|
||
<input type="text" id="new-username" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Email:</label>
|
||
<input type="email" id="new-email" required>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Password:</label>
|
||
<input type="password" id="new-password" required minlength="6">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Role:</label>
|
||
<select id="new-role">
|
||
<option value="listener">Listener</option>
|
||
<option value="dj">DJ</option>
|
||
<option value="admin">Admin</option>
|
||
</select>
|
||
</div>
|
||
<div class="controls">
|
||
<button type="submit" class="btn btn-success">Create User</button>
|
||
<button type="button" class="btn btn-secondary" onclick="toggleCreateUserForm()">Cancel</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- User List Container (populated by JavaScript) -->
|
||
<div class="admin-section" id="users-list-section" style="display: none;">
|
||
<h2>All Users</h2>
|
||
<div id="users-container">
|
||
<!-- Users table will be inserted here by JavaScript -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// User Management JavaScript
|
||
|
||
// Load user stats on page load
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadUserStats();
|
||
});
|
||
|
||
async function loadUserStats() {
|
||
try {
|
||
const response = await fetch('/asteroid/api/users/stats');
|
||
const result = await response.json();
|
||
|
||
if (result.status === 'success') {
|
||
const stats = result.stats;
|
||
document.getElementById('total-users').textContent = stats.total;
|
||
document.getElementById('active-users').textContent = stats.active;
|
||
document.getElementById('admin-users').textContent = stats.admins;
|
||
document.getElementById('dj-users').textContent = stats.djs;
|
||
}
|
||
} catch (error) {
|
||
console.error('Error loading user stats:', error);
|
||
}
|
||
}
|
||
|
||
async function loadUsers() {
|
||
try {
|
||
const response = await fetch('/asteroid/api/users');
|
||
const result = await response.json();
|
||
|
||
if (result.status === 'success') {
|
||
showUsersTable(result.users);
|
||
document.getElementById('users-list-section').style.display = 'block';
|
||
}
|
||
} catch (error) {
|
||
console.error('Error loading users:', error);
|
||
alert('Error loading users. Please try again.');
|
||
}
|
||
}
|
||
|
||
function showUsersTable(users) {
|
||
const container = document.getElementById('users-container');
|
||
container.innerHTML = `
|
||
<table class="users-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Username</th>
|
||
<th>Email</th>
|
||
<th>Role</th>
|
||
<th>Status</th>
|
||
<th>Last Login</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
${users.map(user => `
|
||
<tr>
|
||
<td>${user.username}</td>
|
||
<td>${user.email}</td>
|
||
<td>
|
||
<select onchange="updateUserRole('${user.id}', this.value)">
|
||
<option value="listener" ${user.role === 'listener' ? 'selected' : ''}>Listener</option>
|
||
<option value="dj" ${user.role === 'dj' ? 'selected' : ''}>DJ</option>
|
||
<option value="admin" ${user.role === 'admin' ? 'selected' : ''}>Admin</option>
|
||
</select>
|
||
</td>
|
||
<td>${user.active ? '✅ Active' : '❌ Inactive'}</td>
|
||
<td>${user['last-login'] ? new Date(user['last-login'] * 1000).toLocaleString() : 'Never'}</td>
|
||
<td class="user-actions">
|
||
${user.active ?
|
||
`<button class="btn btn-danger" onclick="deactivateUser('${user.id}')">Deactivate</button>` :
|
||
`<button class="btn btn-success" onclick="activateUser('${user.id}')">Activate</button>`
|
||
}
|
||
</td>
|
||
</tr>
|
||
`).join('')}
|
||
</tbody>
|
||
</table>
|
||
<button class="btn btn-secondary" onclick="hideUsersTable()">Close</button>
|
||
`;
|
||
}
|
||
|
||
function hideUsersTable() {
|
||
document.getElementById('users-list-section').style.display = 'none';
|
||
}
|
||
|
||
async function updateUserRole(userId, newRole) {
|
||
try {
|
||
const formData = new FormData();
|
||
formData.append('role', newRole);
|
||
|
||
const response = await fetch(`/asteroid/api/users/${userId}/role`, {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.status === 'success') {
|
||
loadUserStats();
|
||
alert('User role updated successfully');
|
||
} else {
|
||
alert('Error updating user role: ' + result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error updating user role:', error);
|
||
alert('Error updating user role. Please try again.');
|
||
}
|
||
}
|
||
|
||
async function deactivateUser(userId) {
|
||
if (!confirm('Are you sure you want to deactivate this user?')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`/asteroid/api/users/${userId}/deactivate`, {
|
||
method: 'POST'
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.status === 'success') {
|
||
loadUsers();
|
||
loadUserStats();
|
||
alert('User deactivated successfully');
|
||
} else {
|
||
alert('Error deactivating user: ' + result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error deactivating user:', error);
|
||
alert('Error deactivating user. Please try again.');
|
||
}
|
||
}
|
||
|
||
async function activateUser(userId) {
|
||
try {
|
||
const response = await fetch(`/asteroid/api/users/${userId}/activate`, {
|
||
method: 'POST'
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.status === 'success') {
|
||
loadUsers();
|
||
loadUserStats();
|
||
alert('User activated successfully');
|
||
} else {
|
||
alert('Error activating user: ' + result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error activating user:', error);
|
||
alert('Error activating user. Please try again.');
|
||
}
|
||
}
|
||
|
||
function toggleCreateUserForm() {
|
||
const form = document.getElementById('create-user-form');
|
||
if (form.style.display === 'none') {
|
||
form.style.display = 'block';
|
||
// Clear form
|
||
document.getElementById('new-username').value = '';
|
||
document.getElementById('new-email').value = '';
|
||
document.getElementById('new-password').value = '';
|
||
document.getElementById('new-role').value = 'listener';
|
||
} else {
|
||
form.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
async function createNewUser(event) {
|
||
event.preventDefault();
|
||
|
||
const username = document.getElementById('new-username').value;
|
||
const email = document.getElementById('new-email').value;
|
||
const password = document.getElementById('new-password').value;
|
||
const role = document.getElementById('new-role').value;
|
||
|
||
try {
|
||
const formData = new FormData();
|
||
formData.append('username', username);
|
||
formData.append('email', email);
|
||
formData.append('password', password);
|
||
formData.append('role', role);
|
||
|
||
const response = await fetch('/asteroid/api/users/create', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.status === 'success') {
|
||
alert(`User "${username}" created successfully!`);
|
||
toggleCreateUserForm();
|
||
loadUserStats();
|
||
loadUsers();
|
||
} else {
|
||
alert('Error creating user: ' + result.message);
|
||
}
|
||
} catch (error) {
|
||
console.error('Error creating user:', error);
|
||
alert('Error creating user. Please try again.');
|
||
}
|
||
}
|
||
|
||
function refreshStats() {
|
||
loadUserStats();
|
||
alert('Stats refreshed!');
|
||
}
|
||
|
||
// Update user stats every 30 seconds
|
||
setInterval(loadUserStats, 30000);
|
||
</script>
|
||
</body>
|
||
</html>
|