166 lines
3.5 KiB
HTML
166 lines
3.5 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Admin - GoalsBreakDown{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.admin-page {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.admin-header {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.admin-header h1 {
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.users-table {
|
|
width: 100%;
|
|
background: white;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.users-table table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.users-table th,
|
|
.users-table td {
|
|
padding: 1rem;
|
|
text-align: left;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.users-table th {
|
|
background: #f8f9fa;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.users-table tr:last-child td {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.users-table input[type="number"] {
|
|
width: 80px;
|
|
padding: 0.5rem;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.users-table select {
|
|
padding: 0.5rem;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.save-btn {
|
|
padding: 0.5rem 1rem;
|
|
background-color: #27ae60;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.save-btn:hover {
|
|
background-color: #229954;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="admin-page">
|
|
<div class="admin-header">
|
|
<h1>Admin Panel</h1>
|
|
</div>
|
|
<div class="users-table">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>User ID</th>
|
|
<th>Username</th>
|
|
<th>Role</th>
|
|
<th>Max Goals</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="users-tbody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
let users = [];
|
|
|
|
async function loadUsers() {
|
|
try {
|
|
users = await get("/api/admin/users");
|
|
renderUsers();
|
|
} catch (error) {
|
|
console.error("Failed to load users:", error);
|
|
}
|
|
}
|
|
|
|
function renderUsers() {
|
|
const tbody = document.getElementById("users-tbody");
|
|
tbody.innerHTML = users.map(user => `
|
|
<tr>
|
|
<td>${user.user_id}</td>
|
|
<td>${escapeHtml(user.username)}</td>
|
|
<td>
|
|
<select onchange="updateUserRole(${user.user_id}, this.value)">
|
|
<option value="user" ${user.role === 'user' ? 'selected' : ''}>User</option>
|
|
<option value="admin" ${user.role === 'admin' ? 'selected' : ''}>Admin</option>
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<input type="number" min="1" value="${user.max_goals}"
|
|
id="max-goals-${user.user_id}" autocomplete="off">
|
|
</td>
|
|
<td>
|
|
<button class="save-btn" onclick="saveUser(${user.user_id})">Save</button>
|
|
</td>
|
|
</tr>
|
|
`).join("");
|
|
}
|
|
|
|
async function saveUser(userId) {
|
|
const maxGoals = parseInt(document.getElementById(`max-goals-${userId}`).value);
|
|
|
|
try {
|
|
await put(`/api/admin/users/${userId}`, { max_goals });
|
|
await loadUsers();
|
|
} catch (error) {
|
|
alert(error.message);
|
|
}
|
|
}
|
|
|
|
async function updateUserRole(userId, role) {
|
|
try {
|
|
await put(`/api/admin/users/${userId}`, { role });
|
|
await loadUsers();
|
|
} catch (error) {
|
|
alert(error.message);
|
|
}
|
|
}
|
|
|
|
function escapeHtml(text) {
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
loadUsers();
|
|
});
|
|
</script>
|
|
{% endblock %}
|