Some rows (schema drift, cancelled requests, manual status edits) have customer_approved = NULL instead of the default 'none'. Templates called .upper() on it blindly, causing a Jinja2 UndefinedError / 500 when the customer player or admin pages loaded. Replace all .upper() calls with (value or 'none').upper() and add truthy checks before rendering the approved choice in status.html and dashboard.html.
183 lines
5.9 KiB
HTML
183 lines
5.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<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.
|
|
Shows all requests in a table with status filters, per-row Open/Delete
|
|
actions, a Settings link, auto-refresh hint, and logout.
|
|
*/
|
|
body{
|
|
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
background:#111827;
|
|
color:#f3f4f6;
|
|
margin:0;
|
|
padding:1rem;
|
|
line-height:1.5;
|
|
}
|
|
.container{max-width:1100px;margin:0 auto;}
|
|
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;
|
|
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;
|
|
}
|
|
table{
|
|
width:100%;
|
|
border-collapse:collapse;
|
|
background:#1f2937;
|
|
border-radius:.5rem;
|
|
overflow:hidden;
|
|
}
|
|
th,td{padding:.7rem;text-align:left;border-bottom:1px solid #374151;}
|
|
th{background:#111827;color:#9ca3af;}
|
|
tr:hover{background:#2d3748;}
|
|
.status-badge{
|
|
display:inline-block;
|
|
padding:.25rem .6rem;
|
|
border-radius:9999px;
|
|
font-size:.8rem;
|
|
font-weight:600;
|
|
background:#374151;
|
|
}
|
|
.awaiting_payment{background:#f59e0b;color:#000;}
|
|
.paid,.delivered{background:#10b981;color:#000;}
|
|
.pending,.prompt_ready{background:#60a5fa;color:#000;}
|
|
.songs_uploaded{background:#a78bfa;color:#000;}
|
|
.revisions_requested{background:#f87171;color:#000;}
|
|
.actions a{
|
|
color:#93c5fd;
|
|
text-decoration:none;
|
|
margin-right:.8rem;
|
|
}
|
|
.actions button.delete{
|
|
background:transparent;
|
|
color:#f87171;
|
|
border:none;
|
|
padding:0;
|
|
cursor:pointer;
|
|
font:inherit;
|
|
text-decoration:none;
|
|
margin-right:.8rem;
|
|
}
|
|
.logout{float:right;color:#f87171;text-decoration:none;}
|
|
.flash{
|
|
padding:.8rem;
|
|
background:#064e3b;
|
|
border-radius:.5rem;
|
|
margin-bottom:1rem;
|
|
}
|
|
.flash.error{background:#450a0a;}
|
|
|
|
/* Topbar with Settings and Log out link */
|
|
.topbar{
|
|
float:right;
|
|
display:flex;
|
|
gap:.75rem;
|
|
align-items:center;
|
|
}
|
|
.topbar form{display:inline;}
|
|
.topbar a{
|
|
color:#93c5fd;
|
|
text-decoration:none;
|
|
}
|
|
.topbar a.settings{
|
|
color:#10b981;
|
|
}
|
|
|
|
.refresh-hint{
|
|
color:#6b7280;
|
|
font-size:.85rem;
|
|
margin-left:auto;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<!-- Topbar: Kiosk, Pricing, Sales, Settings, Log out -->
|
|
<div class="topbar">
|
|
<a href="{{ url_for('kiosk') }}" target="_blank" rel="noopener noreferrer">Kiosk</a>
|
|
<a href="{{ url_for('admin_pricing') }}">Pricing</a>
|
|
<a href="{{ url_for('admin_sales') }}">Sales</a>
|
|
<a href="{{ url_for('admin_settings') }}" class="settings">Settings</a>
|
|
<a href="{{ url_for('admin_logout') }}" class="logout">Log out</a>
|
|
</div>
|
|
|
|
<h1>Theme Song Booth — Admin Dashboard</h1>
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% for category, message in messages %}
|
|
<div class="flash {{ category }}">{{ message }}</div>
|
|
{% endfor %}
|
|
{% endwith %}
|
|
|
|
<!-- Status filter links -->
|
|
<div class="filters">
|
|
<a href="{{ url_for('admin_dashboard') }}" class="{% if current_status in (None, '') %}active{% endif %}">All</a>
|
|
<a href="{{ url_for('admin_dashboard', status='pending') }}" class="{% if current_status == 'pending' %}active{% endif %}">Pending</a>
|
|
<a href="{{ url_for('admin_dashboard', status='songs_uploaded') }}" class="{% if current_status == 'songs_uploaded' %}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='delivered') }}" class="{% if current_status == 'delivered' %}active{% endif %}">Delivered</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 -->
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Name</th>
|
|
<th>Email</th>
|
|
<th>Genre</th>
|
|
<th>Status</th>
|
|
<th>Approved</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for r in requests %}
|
|
<tr>
|
|
<td>#{{ r.id }}</td>
|
|
<td>{{ r.name }}</td>
|
|
<td>{{ r.email }}</td>
|
|
<td>{{ r.style_genre or '-' }}</td>
|
|
<td><span class="status-badge {{ r.status }}">{{ statuses[r.status] }}</span></td>
|
|
<td>{% if r.customer_approved and r.customer_approved != 'none' %}{{ (r.customer_approved or 'none').upper() }}{% else %}-{% endif %}</td>
|
|
<td class="actions">
|
|
<a href="{{ url_for('admin_request', rid=r.id) }}">Open</a>
|
|
<form method="POST" action="{{ url_for('admin_delete_request', rid=r.id) }}" style="display:inline" onsubmit="return confirm('ARE YOU SURE you want to delete request #{{ r.id }} for {{ r.name }}? This cannot be undone.')">
|
|
<button type="submit" class="delete">Delete</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% if not requests %}
|
|
<tr><td colspan="7">No requests found.</td></tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</body>
|
|
</html>
|