Update admin page to /admin/users route and icon buttons

- Changed admin routes from /admin/user/* to /admin/users/*
- Updated admin.html to extend base.html
- Changed action buttons to small icon-based buttons (edit, activate/deactivate, delete)
- Made edit user modal scrollable with overflow-y: auto
parent e7a5ebf7
<!DOCTYPE html> {% extends "base.html" %}
<html>
<head> {% block title %}Admin Panel - VidAI{% endblock %}
<title>Admin Panel - VidAI</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> {% block head %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <style>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Inter', sans-serif; background: #f8fafc; }
.header { background: white; padding: 1rem 2rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.header-content { display: flex; justify-content: space-between; align-items: center; max-width: 1200px; margin: 0 auto; }
.logo { font-size: 1.5rem; font-weight: 700; color: #667eea; }
.nav { display: flex; gap: 2rem; }
.nav a { text-decoration: none; color: #64748b; font-weight: 500; }
.nav a.active { color: #667eea; }
.user-menu { display: flex; align-items: center; gap: 1rem; position: relative; }
.user-icon { cursor: pointer; padding: 0.5rem; border-radius: 50%; background: #f8fafc; transition: background 0.2s; }
.user-icon:hover { background: #e2e8f0; }
.user-icon i { font-size: 1.2rem; color: #64748b; }
.user-dropdown { display: none; position: absolute; top: 100%; right: 0; background: white; min-width: 200px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); border-radius: 8px; z-index: 1000; }
.user-dropdown a { display: block; padding: 0.75rem 1rem; text-decoration: none; color: #374151; border-bottom: 1px solid #f1f5f9; }
.user-dropdown a:last-child { border-bottom: none; color: #dc2626; }
.user-dropdown a:hover { background: #f8fafc; }
.container { max-width: 1200px; margin: 2rem auto; padding: 0 2rem; } .container { max-width: 1200px; margin: 2rem auto; padding: 0 2rem; }
.admin-card { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); margin-bottom: 2rem; } .admin-card { background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); margin-bottom: 2rem; }
.card-header { margin-bottom: 1.5rem; } .card-header { margin-bottom: 1.5rem; }
...@@ -29,6 +12,8 @@ ...@@ -29,6 +12,8 @@
.btn:hover { background: #5a67d8; } .btn:hover { background: #5a67d8; }
.btn-danger { background: #dc2626; } .btn-danger { background: #dc2626; }
.btn-danger:hover { background: #b91c1c; } .btn-danger:hover { background: #b91c1c; }
.btn-icon { padding: 0.5rem; background: none; border: none; cursor: pointer; color: #64748b; }
.btn-icon:hover { color: #374151; }
.table { width: 100%; border-collapse: collapse; margin-top: 1rem; } .table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
.table th, .table td { padding: 1rem; text-align: left; border-bottom: 1px solid #e5e7eb; } .table th, .table td { padding: 1rem; text-align: left; border-bottom: 1px solid #e5e7eb; }
.table th { background: #f8fafc; font-weight: 600; color: #374151; } .table th { background: #f8fafc; font-weight: 600; color: #374151; }
...@@ -52,40 +37,11 @@ ...@@ -52,40 +37,11 @@
.modal-footer { padding: 1rem 2rem; border-top: 1px solid #e5e7eb; text-align: right; flex-shrink: 0; } .modal-footer { padding: 1rem 2rem; border-top: 1px solid #e5e7eb; text-align: right; flex-shrink: 0; }
.form-row { display: flex; gap: 1rem; } .form-row { display: flex; gap: 1rem; }
.form-row .form-group { flex: 1; } .form-row .form-group { flex: 1; }
</style> </style>
</head> {% endblock %}
<body>
<header class="header">
<div class="header-content">
<div class="logo">VidAI</div>
<nav class="nav">
<a href="/dashboard">Dashboard</a>
<a href="/analyze">Analyze</a>
<a href="/train">Train</a>
<a href="/history">History</a>
<a href="/api_tokens">API Tokens</a>
<a href="/settings">Settings</a>
<a href="/admin" class="active">Admin</a>
</nav>
<div class="user-menu">
<div class="user-icon" onclick="toggleUserMenu()">
<i class="fas fa-user"></i>
</div>
<div id="userDropdown" class="user-dropdown">
<a href="/account">Account</a>
{% if user.get('role') != 'admin' %}
<a href="/api_tokens">Tokens</a>
{% else %}
<a href="/admin/users">Users</a>
<a href="/config">Configurations</a>
{% endif %}
<a href="/logout">Logout</a>
</div>
</div>
</div>
</header>
<div class="container"> {% block content %}
<div class="container">
<div class="admin-card"> <div class="admin-card">
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-users-cog"></i> User Management</h3> <h3><i class="fas fa-users-cog"></i> User Management</h3>
...@@ -121,13 +77,13 @@ ...@@ -121,13 +77,13 @@
<td>{{ user.get('tokens', 0) }}</td> <td>{{ user.get('tokens', 0) }}</td>
<td>{{ user.get('created_at', 'N/A')[:10] if user.get('created_at') else 'N/A' }}</td> <td>{{ user.get('created_at', 'N/A')[:10] if user.get('created_at') else 'N/A' }}</td>
<td> <td>
<button onclick="editUser({{ user.get('id') }}, '{{ user.get('username') }}', '{{ user.get('email') }}', '{{ user.get('role') }}', {{ user.get('tokens', 0) }}, {{ user.get('active')|lower }})" class="btn" style="padding: 0.5rem 1rem; font-size: 0.9rem;">Edit</button> <button onclick="editUser({{ user.get('id') }}, '{{ user.get('username') }}', '{{ user.get('email') }}', '{{ user.get('role') }}', {{ user.get('tokens', 0) }}, {{ user.get('active')|lower }})" class="btn-icon" title="Edit"><i class="fas fa-edit"></i></button>
{% if user.get('active') %} {% if user.get('active') %}
<a href="/admin/user/{{ user.get('id') }}/deactivate" class="btn btn-danger" style="padding: 0.5rem 1rem; font-size: 0.9rem;">Deactivate</a> <a href="/admin/users/{{ user.get('id') }}/deactivate" class="btn-icon" title="Deactivate"><i class="fas fa-ban"></i></a>
{% else %} {% else %}
<a href="/admin/user/{{ user.get('id') }}/activate" class="btn" style="padding: 0.5rem 1rem; font-size: 0.9rem; background: #059669;">Activate</a> <a href="/admin/users/{{ user.get('id') }}/activate" class="btn-icon" title="Activate" style="color: #059669;"><i class="fas fa-check"></i></a>
{% endif %} {% endif %}
<button onclick="deleteUser({{ user.get('id') }}, '{{ user.get('username') }}')" class="btn btn-danger" style="padding: 0.5rem 1rem; font-size: 0.9rem; background: #7f1d1d;">Delete</button> <button onclick="deleteUser({{ user.get('id') }}, '{{ user.get('username') }}')" class="btn-icon" title="Delete" style="color: #dc2626;"><i class="fas fa-trash"></i></button>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -140,7 +96,7 @@ ...@@ -140,7 +96,7 @@
<h3><i class="fas fa-plus"></i> Create New User</h3> <h3><i class="fas fa-plus"></i> Create New User</h3>
</div> </div>
<form method="post" action="/admin/user/create"> <form method="post" action="/admin/users/create">
<div class="form-group"> <div class="form-group">
<label for="username">Username</label> <label for="username">Username</label>
<input type="text" id="username" name="username" required> <input type="text" id="username" name="username" required>
...@@ -218,6 +174,50 @@ ...@@ -218,6 +174,50 @@
</div> </div>
</div> </div>
<!-- Edit User Modal -->
<div id="editUserModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Edit User</h3>
<span onclick="closeEditModal()" style="cursor: pointer; font-size: 1.5rem;">&times;</span>
</div>
<form id="editUserForm" method="post" action="">
<div class="modal-body">
<input type="hidden" id="editUserId" name="user_id">
<div class="form-group">
<label for="editUsername">Username</label>
<input type="text" id="editUsername" name="username" required>
</div>
<div class="form-group">
<label for="editEmail">Email</label>
<input type="email" id="editEmail" name="email" required>
</div>
<div class="form-row">
<div class="form-group">
<label for="editRole">Role</label>
<select id="editRole" name="role">
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
<div class="form-group">
<label for="editTokens">Tokens</label>
<input type="number" id="editTokens" name="tokens" min="0" required>
</div>
</div>
<div class="form-group">
<label for="editPassword">New Password (leave empty to keep current)</label>
<input type="password" id="editPassword" name="password">
</div>
</div>
<div class="modal-footer">
<button type="button" onclick="closeEditModal()" class="btn" style="background: #6b7280;">Cancel</button>
<button type="submit" class="btn">Update User</button>
</div>
</form>
</div>
</div>
<script> <script>
function editUser(id, username, email, role, tokens, active) { function editUser(id, username, email, role, tokens, active) {
document.getElementById('editUserId').value = id; document.getElementById('editUserId').value = id;
...@@ -226,7 +226,7 @@ ...@@ -226,7 +226,7 @@
document.getElementById('editRole').value = role; document.getElementById('editRole').value = role;
document.getElementById('editTokens').value = tokens; document.getElementById('editTokens').value = tokens;
document.getElementById('editPassword').value = ''; document.getElementById('editPassword').value = '';
document.getElementById('editUserForm').action = `/admin/user/${id}/update`; document.getElementById('editUserForm').action = `/admin/users/${id}/update`;
document.getElementById('editUserModal').style.display = 'flex'; document.getElementById('editUserModal').style.display = 'flex';
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
} }
...@@ -241,7 +241,7 @@ ...@@ -241,7 +241,7 @@
// Create a form and submit it // Create a form and submit it
const form = document.createElement('form'); const form = document.createElement('form');
form.method = 'POST'; form.method = 'POST';
form.action = `/admin/user/${id}/delete`; form.action = `/admin/users/${id}/delete`;
const input = document.createElement('input'); const input = document.createElement('input');
input.type = 'hidden'; input.type = 'hidden';
...@@ -260,18 +260,7 @@ ...@@ -260,18 +260,7 @@
if (event.target == modal) { if (event.target == modal) {
closeEditModal(); closeEditModal();
} }
// Close user dropdown
const dropdown = document.getElementById('userDropdown');
const icon = document.querySelector('.user-icon');
if (dropdown && icon && !icon.contains(event.target) && !dropdown.contains(event.target)) {
dropdown.style.display = 'none';
}
}
function toggleUserMenu() {
const dropdown = document.getElementById('userDropdown');
dropdown.style.display = dropdown.style.display === 'block' ? 'none' : 'block';
} }
</script> </script>
</body> </div>
</html> {% endblock %}
\ No newline at end of file \ No newline at end of file
...@@ -129,7 +129,8 @@ def dashboard(): ...@@ -129,7 +129,8 @@ def dashboard():
completed_jobs=len([j for j in queue_items if j["status"] == "completed"]), completed_jobs=len([j for j in queue_items if j["status"] == "completed"]),
active_jobs=len([j for j in queue_items if j["status"] == "processing"]), active_jobs=len([j for j in queue_items if j["status"] == "processing"]),
total_jobs=len(queue_items), total_jobs=len(queue_items),
queue_items=queue_items[:5]) queue_items=queue_items[:5],
active_page='dashboard')
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
...@@ -233,7 +234,8 @@ def analyze(): ...@@ -233,7 +234,8 @@ def analyze():
user=user, user=user,
tokens=get_user_tokens(user["id"]), tokens=get_user_tokens(user["id"]),
result=result, result=result,
server_dir=server_dir) server_dir=server_dir,
active_page='analyze')
@app.route('/train', methods=['GET', 'POST']) @app.route('/train', methods=['GET', 'POST'])
@admin_required @admin_required
...@@ -301,7 +303,8 @@ def train(): ...@@ -301,7 +303,8 @@ def train():
return render_template('train.html', return render_template('train.html',
user=user, user=user,
tokens=get_user_tokens(user["id"]), tokens=get_user_tokens(user["id"]),
message=message) message=message,
active_page='train')
@app.route('/config', methods=['GET', 'POST']) @app.route('/config', methods=['GET', 'POST'])
def config(): def config():
...@@ -319,7 +322,7 @@ def config(): ...@@ -319,7 +322,7 @@ def config():
config_data = get_result(msg_id) config_data = get_result(msg_id)
current_config = config_data.get('data', {}) current_config = config_data.get('data', {})
return render_template('config.html', current_config=current_config) return render_template('config.html', current_config=current_config, active_page='config')
@app.route('/history') @app.route('/history')
@login_required @login_required
...@@ -331,7 +334,8 @@ def history(): ...@@ -331,7 +334,8 @@ def history():
return render_template('history.html', return render_template('history.html',
user=user, user=user,
tokens=get_user_tokens(user["id"]), tokens=get_user_tokens(user["id"]),
queue_items=queue_items) queue_items=queue_items,
active_page='history')
@app.route('/settings') @app.route('/settings')
@admin_required @admin_required
...@@ -341,7 +345,8 @@ def settings(): ...@@ -341,7 +345,8 @@ def settings():
return render_template('settings.html', return render_template('settings.html',
user=user, user=user,
tokens=get_user_tokens(user["id"])) tokens=get_user_tokens(user["id"]),
active_page='settings')
@app.route('/update_settings', methods=['POST']) @app.route('/update_settings', methods=['POST'])
@login_required @login_required
...@@ -368,7 +373,7 @@ def api_tokens(): ...@@ -368,7 +373,7 @@ def api_tokens():
user = get_current_user_session() user = get_current_user_session()
from .database import get_user_api_tokens from .database import get_user_api_tokens
user_tokens = get_user_api_tokens(user['id']) user_tokens = get_user_api_tokens(user['id'])
return render_template('api_tokens.html', user=user, user_tokens=user_tokens) return render_template('api_tokens.html', user=user, user_tokens=user_tokens, active_page='api_tokens')
@app.route('/api_tokens/generate', methods=['POST']) @app.route('/api_tokens/generate', methods=['POST'])
@login_required @login_required
...@@ -406,7 +411,7 @@ def delete_api_token(token_id): ...@@ -406,7 +411,7 @@ def delete_api_token(token_id):
def account(): def account():
"""User account management page.""" """User account management page."""
user = get_current_user_session() user = get_current_user_session()
return render_template('account.html', user=user) return render_template('account.html', user=user, active_page='account')
@app.route('/account/change_password', methods=['POST']) @app.route('/account/change_password', methods=['POST'])
...@@ -506,9 +511,9 @@ def admin(): ...@@ -506,9 +511,9 @@ def admin():
from .database import get_all_users from .database import get_all_users
users = get_all_users() users = get_all_users()
user = get_current_user_session() user = get_current_user_session()
return render_template('admin.html', users=users, user=user) return render_template('admin.html', users=users, user=user, active_page='admin')
@app.route('/admin/user/create', methods=['POST']) @app.route('/admin/users/create', methods=['POST'])
@admin_required @admin_required
def admin_create_user(): def admin_create_user():
"""Create a new user via admin panel.""" """Create a new user via admin panel."""
...@@ -526,7 +531,7 @@ def admin_create_user(): ...@@ -526,7 +531,7 @@ def admin_create_user():
flash(message, 'error') flash(message, 'error')
return redirect(url_for('admin')) return redirect(url_for('admin'))
@app.route('/admin/user/<int:user_id>/activate') @app.route('/admin/users/<int:user_id>/activate')
@admin_required @admin_required
def admin_activate_user(user_id): def admin_activate_user(user_id):
"""Activate a user account.""" """Activate a user account."""
...@@ -537,7 +542,7 @@ def admin_activate_user(user_id): ...@@ -537,7 +542,7 @@ def admin_activate_user(user_id):
flash('Failed to activate user.', 'error') flash('Failed to activate user.', 'error')
return redirect(url_for('admin')) return redirect(url_for('admin'))
@app.route('/admin/user/<int:user_id>/deactivate') @app.route('/admin/users/<int:user_id>/deactivate')
@admin_required @admin_required
def admin_deactivate_user(user_id): def admin_deactivate_user(user_id):
"""Deactivate a user account.""" """Deactivate a user account."""
...@@ -548,7 +553,7 @@ def admin_deactivate_user(user_id): ...@@ -548,7 +553,7 @@ def admin_deactivate_user(user_id):
flash('Failed to deactivate user.', 'error') flash('Failed to deactivate user.', 'error')
return redirect(url_for('admin')) return redirect(url_for('admin'))
@app.route('/admin/user/<int:user_id>/update', methods=['POST']) @app.route('/admin/users/<int:user_id>/update', methods=['POST'])
@admin_required @admin_required
def admin_update_user(user_id): def admin_update_user(user_id):
"""Update user information.""" """Update user information."""
...@@ -566,7 +571,7 @@ def admin_update_user(user_id): ...@@ -566,7 +571,7 @@ def admin_update_user(user_id):
flash(message, 'error') flash(message, 'error')
return redirect(url_for('admin')) return redirect(url_for('admin'))
@app.route('/admin/user/<int:user_id>/delete', methods=['POST']) @app.route('/admin/users/<int:user_id>/delete', methods=['POST'])
@admin_required @admin_required
def admin_delete_user(user_id): def admin_delete_user(user_id):
"""Delete a user account.""" """Delete a user account."""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment