180 lines
8.1 KiB
HTML
180 lines
8.1 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Manage Users - DosVault{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="mb-8">
|
|
<div class="flex justify-between items-center">
|
|
<div>
|
|
<h1 class="text-3xl font-bold mb-2">Manage Users</h1>
|
|
<p class="text-gray-400">Create and manage user accounts</p>
|
|
</div>
|
|
<button onclick="showCreateUser()" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded">
|
|
Create New User
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-800 rounded-lg border border-gray-700 overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead class="bg-gray-700">
|
|
<tr>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">User</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Role</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Status</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Created</th>
|
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-300 uppercase tracking-wider">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-700">
|
|
{% for user in users %}
|
|
<tr class="hover:bg-gray-700">
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<div>
|
|
<p class="text-sm font-medium text-white">{{ user.username }}</p>
|
|
<p class="text-sm text-gray-400">{{ user.email }}</p>
|
|
</div>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="px-2 py-1 rounded text-xs
|
|
{% if user.role == 'super' %}bg-red-600
|
|
{% elif user.role == 'normal' %}bg-blue-600
|
|
{% else %}bg-yellow-600{% endif %}">
|
|
{{ user.role.upper() }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
<span class="px-2 py-1 rounded text-xs
|
|
{% if user.is_active %}bg-green-600{% else %}bg-gray-600{% endif %}">
|
|
{% if user.is_active %}ACTIVE{% else %}INACTIVE{% endif %}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-300">
|
|
{{ user.created_at.strftime('%Y-%m-%d') if user.created_at else 'N/A' }}
|
|
</td>
|
|
<td class="px-6 py-4 whitespace-nowrap text-sm">
|
|
<div class="flex space-x-2">
|
|
{% if user.id != current_user.id %}
|
|
<button onclick="toggleUserActive({{ user.id }})"
|
|
class="{% if user.is_active %}text-red-400 hover:text-red-300{% else %}text-green-400 hover:text-green-300{% endif %}">
|
|
{% if user.is_active %}Deactivate{% else %}Activate{% endif %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if total_pages > 1 %}
|
|
<div class="mt-8 flex justify-center">
|
|
<nav class="flex items-center space-x-2">
|
|
{% if current_page > 1 %}
|
|
<a href="?page={{ current_page - 1 }}"
|
|
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm">
|
|
Previous
|
|
</a>
|
|
{% endif %}
|
|
|
|
{% for page_num in range(1, total_pages + 1) %}
|
|
{% if page_num == current_page %}
|
|
<span class="px-3 py-2 bg-blue-600 rounded text-sm">{{ page_num }}</span>
|
|
{% elif page_num <= current_page + 2 and page_num >= current_page - 2 %}
|
|
<a href="?page={{ page_num }}"
|
|
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm">
|
|
{{ page_num }}
|
|
</a>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if current_page < total_pages %}
|
|
<a href="?page={{ current_page + 1 }}"
|
|
class="px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded text-sm">
|
|
Next
|
|
</a>
|
|
{% endif %}
|
|
</nav>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Create User Modal -->
|
|
<div id="createUserModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
|
|
<div class="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-gray-800 border-gray-700">
|
|
<div class="mt-3">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h3 class="text-lg font-medium text-white">Create New User</h3>
|
|
<button onclick="hideCreateUser()" class="text-gray-400 hover:text-white">×</button>
|
|
</div>
|
|
<form method="POST" class="space-y-4">
|
|
<div>
|
|
<input type="text" name="username" placeholder="Username" required
|
|
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<input type="email" name="email" placeholder="Email" required
|
|
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<input type="password" name="password" placeholder="Password" required
|
|
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
</div>
|
|
<div>
|
|
<select name="role" required
|
|
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-md text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
|
|
<option value="">Select Role</option>
|
|
<option value="demo">Demo User</option>
|
|
<option value="normal">Normal User</option>
|
|
<option value="super">Super User</option>
|
|
</select>
|
|
</div>
|
|
<div class="flex justify-between">
|
|
<button type="button" onclick="hideCreateUser()" class="px-4 py-2 bg-gray-600 hover:bg-gray-700 rounded-md">
|
|
Cancel
|
|
</button>
|
|
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 rounded-md">
|
|
Create User
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function showCreateUser() {
|
|
document.getElementById('createUserModal').classList.remove('hidden');
|
|
}
|
|
|
|
function hideCreateUser() {
|
|
document.getElementById('createUserModal').classList.add('hidden');
|
|
}
|
|
|
|
async function toggleUserActive(userId) {
|
|
const token = localStorage.getItem('authToken');
|
|
if (!token) return;
|
|
|
|
try {
|
|
const response = await fetch(`/admin/users/${userId}/toggle-active`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Authorization': `Bearer ${token}`
|
|
}
|
|
});
|
|
|
|
if (response.ok) {
|
|
location.reload();
|
|
} else {
|
|
alert('Failed to update user status');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error toggling user status:', error);
|
|
alert('Failed to update user status');
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %} |