GitHub Actions
Sync from GitHub repo
f1a0148
raw
history blame contribute delete
14.8 kB
{% extends "base.html" %}
{% block title %}Admin Panel - TTS Arena{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.bundle.min.js"></script>
<style>
.admin-container {
width: 100%;
}
/* Horizontal navigation tabs */
.admin-nav {
display: flex;
overflow-x: auto;
white-space: nowrap;
margin-bottom: 24px;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
-ms-overflow-style: none; /* Hide scrollbar IE and Edge */
scrollbar-width: none; /* Hide scrollbar Firefox */
}
/* Hide scrollbar for Chrome, Safari and Opera */
.admin-nav::-webkit-scrollbar {
display: none;
}
.admin-nav-item {
display: flex;
align-items: center;
padding: 10px 16px;
margin-right: 8px;
border-radius: var(--radius);
cursor: pointer;
transition: all 0.2s;
color: var(--text-color);
text-decoration: none;
font-size: 14px;
position: relative;
}
.admin-nav-item.active {
color: var(--primary-color);
font-weight: 500;
}
.admin-nav-item.active::after {
content: '';
position: absolute;
bottom: -9px;
left: 0;
width: 100%;
height: 3px;
background-color: var(--primary-color);
border-radius: 3px 3px 0 0;
}
.admin-nav-item:hover:not(.active) {
background-color: rgba(0, 0, 0, 0.05);
}
.admin-nav-item svg {
margin-right: 8px;
width: 16px;
height: 16px;
}
.admin-content {
width: 100%;
padding: 20px;
}
.admin-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.admin-title {
font-size: 24px;
font-weight: 600;
color: var(--primary-color);
}
.admin-card {
background-color: white;
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 20px;
margin-bottom: 24px;
overflow: auto; /* Add horizontal scrolling for content */
}
.admin-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.admin-card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-color);
}
.admin-stats {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background-color: white;
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 16px;
text-align: center;
}
.stat-title {
font-size: 14px;
color: #666;
margin-bottom: 8px;
}
.stat-value {
font-size: 24px;
font-weight: 600;
color: var(--primary-color);
}
/* Improved table styles with responsiveness */
.table-responsive {
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
margin-bottom: 1rem;
}
.admin-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: var(--radius);
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
border: 1px solid var(--border-color);
min-width: 600px; /* Ensures table doesn't get too squished */
}
.admin-table th, .admin-table td {
padding: 12px 16px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.admin-table th {
font-weight: 600;
background-color: var(--secondary-color);
position: sticky;
top: 0;
z-index: 1;
font-size: 13px;
}
.admin-table tr:last-child td {
border-bottom: none;
}
.admin-table tr:hover {
background-color: rgba(0, 0, 0, 0.02);
}
/* Action buttons in tables */
.action-btn {
display: inline-block;
padding: 6px 12px;
font-size: 13px;
border-radius: var(--radius);
text-decoration: none;
color: var(--text-color);
background-color: var(--secondary-color);
border: 1px solid var(--border-color);
transition: all 0.2s;
}
.action-btn:hover {
background-color: #e0e0e0;
}
/* Enhanced form styles */
.admin-form {
max-width: 700px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-color);
}
.form-group small {
display: block;
margin-top: 4px;
color: #666;
font-size: 12px;
}
.form-control {
width: 100%;
padding: 12px;
border: 1px solid var(--border-color);
border-radius: var(--radius);
font-family: 'Inter', sans-serif;
background-color: white;
transition: border-color 0.2s, box-shadow 0.2s;
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(80, 70, 229, 0.25);
outline: none;
}
/* Custom checkbox styles */
.form-check {
display: flex;
align-items: center;
margin-bottom: 16px;
position: relative;
padding-left: 30px;
cursor: pointer;
}
.form-check input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.form-check label {
margin-bottom: 0;
cursor: pointer;
}
.checkmark {
position: absolute;
top: 2px;
left: 0;
height: 18px;
width: 18px;
background-color: white;
border: 1px solid var(--border-color);
border-radius: 4px;
}
.form-check:hover input ~ .checkmark {
background-color: #f5f5f5;
}
.form-check input:checked ~ .checkmark {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.checkmark:after {
content: "";
position: absolute;
display: none;
}
.form-check input:checked ~ .checkmark:after {
display: block;
}
.form-check .checkmark:after {
left: 6px;
top: 2px;
width: 4px;
height: 9px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.user-info {
background-color: var(--light-gray);
padding: 16px;
border-radius: var(--radius);
margin-bottom: 16px;
border: 1px solid var(--border-color);
}
.user-info p {
margin-bottom: 8px;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: var(--radius);
cursor: pointer;
font-weight: 500;
text-decoration: none;
transition: background-color 0.2s;
}
.btn-primary:hover {
background-color: #4038c7;
}
.btn-secondary {
background-color: var(--secondary-color);
color: var(--text-color);
border: 1px solid var(--border-color);
padding: 12px 20px;
border-radius: var(--radius);
cursor: pointer;
font-weight: 500;
text-decoration: none;
transition: background-color 0.2s;
}
.btn-secondary:hover {
background-color: #e0e0e0;
}
/* Badge styles */
.badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.badge-primary {
background-color: var(--primary-color);
color: white;
}
.badge-secondary {
background-color: var(--secondary-color);
color: var(--text-color);
}
.pagination {
display: flex;
justify-content: center;
list-style: none;
margin-top: 24px;
}
.pagination li {
margin: 0 4px;
}
.pagination li a {
display: block;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: var(--radius);
color: var(--text-color);
text-decoration: none;
}
.pagination li.active a {
background-color: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
/* Responsive adjustments */
@media (max-width: 768px) {
.admin-content {
padding: 16px 12px;
}
.admin-stats {
grid-template-columns: 1fr 1fr;
}
.admin-header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.admin-card {
padding: 15px 10px;
}
.admin-table {
font-size: 13px;
}
.admin-table th, .admin-table td {
padding: 8px 10px;
}
.action-btn {
padding: 4px 8px;
font-size: 12px;
}
.btn-primary, .btn-secondary {
padding: 8px 16px;
font-size: 14px;
display: block;
width: 100%;
text-align: center;
margin-bottom: 8px;
}
}
/* Dark mode adjustments */
@media (prefers-color-scheme: dark) {
.admin-card, .stat-card {
background-color: var(--light-gray);
}
.admin-table th {
background-color: rgba(80, 70, 229, 0.1);
}
.admin-table tr:hover {
background-color: rgba(255, 255, 255, 0.05);
}
.form-control {
background-color: var(--light-gray);
color: var(--text-color);
border-color: rgba(255, 255, 255, 0.1);
}
.checkmark {
background-color: var(--light-gray);
border-color: rgba(255, 255, 255, 0.2);
}
.form-check:hover input ~ .checkmark {
background-color: rgba(255, 255, 255, 0.1);
}
.action-btn {
background-color: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.15);
}
.action-btn:hover {
background-color: rgba(255, 255, 255, 0.15);
}
.btn-secondary {
background-color: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.15);
}
.btn-secondary:hover {
background-color: rgba(255, 255, 255, 0.15);
}
.btn-primary:hover {
background-color: #5d51ff;
}
}
.user-detail-value {
flex: 1;
}
/* Truncation utility class */
.text-truncate {
max-width: 300px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 576px) {
.text-truncate {
max-width: 150px;
}
.admin-stats {
grid-template-columns: 1fr;
}
}
</style>
{% endblock %}
{% block content %}
<div class="admin-container">
<nav class="admin-nav">
<a href="{{ url_for('admin.index') }}" class="admin-nav-item {% if request.endpoint == 'admin.index' %}active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="9" x="3" y="3" rx="1"/><rect width="7" height="5" x="14" y="3" rx="1"/><rect width="7" height="9" x="14" y="12" rx="1"/><rect width="7" height="5" x="3" y="16" rx="1"/></svg>
Dashboard
</a>
<a href="{{ url_for('admin.models') }}" class="admin-nav-item {% if request.endpoint in ['admin.models', 'admin.edit_model', 'admin.add_model'] %}active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1v3M12 20v3M4.2 4.2l2.1 2.1M17.7 17.7l2.1 2.1M1 12h3M20 12h3M4.2 19.8l2.1-2.1M17.7 6.3l2.1-2.1"/></svg>
Models
</a>
<a href="{{ url_for('admin.users') }}" class="admin-nav-item {% if request.endpoint == 'admin.users' or request.endpoint == 'admin.user_detail' %}active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
Users
</a>
<a href="{{ url_for('admin.votes') }}" class="admin-nav-item {% if request.endpoint == 'admin.votes' %}active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
Votes
</a>
<a href="{{ url_for('admin.statistics') }}" class="admin-nav-item {% if request.endpoint == 'admin.statistics' %}active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M18 12V8"/><path d="M13 12v-2"/><path d="M8 12v-5"/></svg>
Statistics
</a>
<a href="{{ url_for('admin.activity') }}" class="admin-nav-item {% if request.endpoint == 'admin.activity' %}active{% endif %}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12h-8v8h8v-8z"/><path d="M3 21V3h18v9"/><path d="M12 3v6H3"/></svg>
Activity
</a>
</nav>
<div class="admin-content">
{% block admin_content %}{% endblock %}
</div>
</div>
{% endblock %}