Add dashboard auto-refresh, status filters, rate limiting, database backup/restore
This commit is contained in:
parent
f32efc42c5
commit
5ebce2baff
5 changed files with 138 additions and 29 deletions
|
|
@ -4,6 +4,9 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Dashboard</title>
|
||||
{% if refresh_seconds and refresh_seconds > 0 %}
|
||||
<meta http-equiv="refresh" content="{{ refresh_seconds }}">
|
||||
{% endif %}
|
||||
<style>
|
||||
/*
|
||||
Operator dashboard queue.
|
||||
|
|
@ -19,14 +22,22 @@
|
|||
line-height:1.5;
|
||||
}
|
||||
.container{max-width:1100px;margin:0 auto;}
|
||||
h1{color:#60a5fa;}
|
||||
.filters{margin-bottom:1rem;}
|
||||
h1{color:#60a5fa;margin-top:0;}
|
||||
.filters{margin-bottom:1rem;display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;}
|
||||
.filters a{
|
||||
color:#93c5fd;
|
||||
text-decoration:none;
|
||||
margin-right:1rem;
|
||||
padding:.35rem .7rem;
|
||||
border-radius:.4rem;
|
||||
border:1px solid transparent;
|
||||
}
|
||||
.filters a:hover{background:#1f2937;}
|
||||
.filters a.active{
|
||||
font-weight:bold;
|
||||
color:#fff;
|
||||
background:#2563eb;
|
||||
border-color:#2563eb;
|
||||
}
|
||||
.filters a.active{font-weight:bold;color:#fff;}
|
||||
table{
|
||||
width:100%;
|
||||
border-collapse:collapse;
|
||||
|
|
@ -74,7 +85,7 @@
|
|||
}
|
||||
.flash.error{background:#450a0a;}
|
||||
|
||||
/* Topbar with Reset System button and Log out link */
|
||||
/* Topbar with Settings and Log out link */
|
||||
.topbar{
|
||||
float:right;
|
||||
display:flex;
|
||||
|
|
@ -82,17 +93,6 @@
|
|||
align-items:center;
|
||||
}
|
||||
.topbar form{display:inline;}
|
||||
.topbar button.reset-sm{
|
||||
background:#dc2626;
|
||||
color:#fff;
|
||||
border:none;
|
||||
border-radius:.4rem;
|
||||
padding:.4rem .8rem;
|
||||
font-weight:600;
|
||||
cursor:pointer;
|
||||
font-size:.9rem;
|
||||
}
|
||||
.topbar button.reset-sm:hover{background:#b91c1c;}
|
||||
.topbar a{
|
||||
color:#93c5fd;
|
||||
text-decoration:none;
|
||||
|
|
@ -101,8 +101,11 @@
|
|||
color:#10b981;
|
||||
}
|
||||
|
||||
/* Hidden legacy reset box (kept CSS class for compatibility, not displayed) */
|
||||
.reset-box{display:none;}
|
||||
.refresh-hint{
|
||||
color:#6b7280;
|
||||
font-size:.85rem;
|
||||
margin-left:auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -123,10 +126,17 @@
|
|||
|
||||
<!-- Status filter links -->
|
||||
<div class="filters">
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="{% if not current_status %}active{% endif %}">All</a>
|
||||
{% for key,label in statuses.items() %}
|
||||
<a href="{{ url_for('admin_dashboard', status=key) }}" class="{% if current_status == key %}active{% endif %}">{{ label }}</a>
|
||||
{% endfor %}
|
||||
<a href="{{ url_for('admin_dashboard') }}" class="{% if current_status in (None, '') %}active{% endif %}">All</a>
|
||||
<a href="{{ url_for('admin_dashboard', status='needs_upload') }}" class="{% if current_status == 'needs_upload' %}active{% endif %}">Needs Upload</a>
|
||||
<a href="{{ url_for('admin_dashboard', status='awaiting_payment') }}" class="{% if current_status == 'awaiting_payment' %}active{% endif %}">Awaiting Payment</a>
|
||||
<a href="{{ url_for('admin_dashboard', status='done') }}" class="{% if current_status == 'done' %}active{% endif %}">Done</a>
|
||||
<span class="refresh-hint">
|
||||
{% if refresh_seconds and refresh_seconds > 0 %}
|
||||
Auto-refresh every {{ refresh_seconds }}s
|
||||
{% else %}
|
||||
Auto-refresh off
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Requests table -->
|
||||
|
|
|
|||
|
|
@ -218,6 +218,22 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Auto refresh -->
|
||||
<div class="section">
|
||||
<h2>Dashboard Auto-Refresh</h2>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="save_refresh">
|
||||
<label for="refresh_seconds">Refresh interval</label>
|
||||
<select id="refresh_seconds" name="refresh_seconds">
|
||||
<option value="0" {% if current_refresh_seconds == 0 %}selected{% endif %}>Off</option>
|
||||
<option value="10" {% if current_refresh_seconds == 10 %}selected{% endif %}>10 seconds</option>
|
||||
<option value="20" {% if current_refresh_seconds == 20 %}selected{% endif %}>20 seconds</option>
|
||||
<option value="30" {% if current_refresh_seconds == 30 %}selected{% endif %}>30 seconds</option>
|
||||
</select>
|
||||
<button type="submit">Save Refresh Interval</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- MP3 metadata defaults -->
|
||||
<div class="section">
|
||||
<h2>MP3 Metadata Tags</h2>
|
||||
|
|
@ -246,6 +262,24 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Database backup / restore -->
|
||||
<div class="section">
|
||||
<h2>Database Backup / Restore</h2>
|
||||
<p class="copy-hint">Download a copy of the SQLite database before the event. Upload a previous backup to restore it; the current database will be renamed as a timestamped backup.</p>
|
||||
|
||||
<form method="POST" style="margin-bottom:1rem">
|
||||
<input type="hidden" name="action" value="download_db">
|
||||
<button type="submit">Download Database Backup</button>
|
||||
</form>
|
||||
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action" value="restore_db">
|
||||
<label for="db_backup">Restore from backup (.db / .sqlite)</label>
|
||||
<input type="file" id="db_backup" name="db_backup" accept=".db,.sqlite,.sqlite3">
|
||||
<button type="submit" onclick="return confirm('This will replace the current database. The old one will be kept as a timestamped backup. Continue?')">Restore Database</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- System reset spans both columns -->
|
||||
<div class="danger-zone">
|
||||
<h2>⚠️ Reset System</h2>
|
||||
|
|
@ -256,7 +290,7 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Reference in a new issue