Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Admin Dashboard - Ticket Collection System</title> | |
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet"> | |
</head> | |
<body> | |
<nav class="navbar navbar-expand-lg"> | |
<div class="container"> | |
<a class="navbar-brand" href="/"> | |
<i class="fas fa-ticket-alt"></i> | |
Ticket Collection System | |
</a> | |
<div class="navbar-nav ms-auto"> | |
<a class="nav-link" href="{{ url_for('index') }}"> | |
<i class="fas fa-home me-1"></i> | |
Home | |
</a> | |
</div> | |
</div> | |
</nav> | |
<div class="container-fluid mt-4"> | |
{% with messages = get_flashed_messages(with_categories=true) %} | |
{% if messages %} | |
{% for category, message in messages %} | |
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert"> | |
{{ message }} | |
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> | |
</div> | |
{% endfor %} | |
{% endif %} | |
{% endwith %} | |
<div class="row mb-4"> | |
<div class="col-12"> | |
<div class="d-flex justify-content-between align-items-center"> | |
<div> | |
<h1 style="font-size: 1.75rem; font-weight: 600; margin: 0; color: var(--text-primary);"> | |
Admin Dashboard | |
</h1> | |
<p style="color: var(--text-secondary); margin: 0.25rem 0 0 0; font-size: 14px;"> | |
Manage and monitor ticket submissions | |
</p> | |
</div> | |
<div class="d-flex gap-2"> | |
<button class="btn btn-outline-secondary btn-sm" onclick="refreshData()"> | |
<i class="fas fa-sync-alt"></i> | |
Refresh | |
</button> | |
{% if tickets %} | |
<a href="{{ url_for('export_excel') }}" class="btn btn-primary btn-sm"> | |
<i class="fas fa-file-excel"></i> | |
Export Excel | |
</a> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="row mb-4"> | |
<div class="col-md-3"> | |
<div class="stats-card"> | |
<div class="stats-icon"> | |
<i class="fas fa-ticket-alt"></i> | |
</div> | |
<div class="stats-content"> | |
<h3>{{ total_tickets }}</h3> | |
<p>Total Tickets</p> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-3"> | |
<div class="stats-card"> | |
<div class="stats-icon"> | |
<i class="fas fa-users"></i> | |
</div> | |
<div class="stats-content"> | |
<h3>{{ tickets|map(attribute='email')|unique|list|length }}</h3> | |
<p>Unique Customers</p> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-3"> | |
<div class="stats-card"> | |
<div class="stats-icon"> | |
<i class="fas fa-globe"></i> | |
</div> | |
<div class="stats-content"> | |
<h3>{{ tickets|map(attribute='country')|unique|list|length }}</h3> | |
<p>Countries</p> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-3"> | |
<div class="stats-card"> | |
<div class="stats-icon"> | |
<i class="fas fa-calendar"></i> | |
</div> | |
<div class="stats-content"> | |
<h3>{{ tickets|selectattr('timestamp')|list|length }}</h3> | |
<p>Records Today</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="row"> | |
<div class="col-12"> | |
<div class="card"> | |
<div class="card-header"> | |
<h5 class="card-title mb-0" style="font-size: 16px; font-weight: 600;"> | |
<i class="fas fa-table me-2" style="color: var(--accent-color);"></i> | |
Ticket Data Records | |
</h5> | |
</div> | |
<div class="card-body"> | |
{% if tickets %} | |
<div class="table-controls mb-3"> | |
<div class="row"> | |
<div class="col-md-6"> | |
<input type="text" id="searchInput" class="form-control" placeholder="Search tickets..."> | |
</div> | |
<div class="col-md-6"> | |
<select id="countryFilter" class="form-select"> | |
<option value="">All Countries</option> | |
{% for country in tickets|map(attribute='country')|unique|sort %} | |
<option value="{{ country }}">{{ country }}</option> | |
{% endfor %} | |
</select> | |
</div> | |
</div> | |
</div> | |
<div class="table-responsive"> | |
<table class="table" id="ticketsTable"> | |
<thead> | |
<tr> | |
<th onclick="sortTable(0)"> | |
Email <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(1)"> | |
Phone <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(2)"> | |
Name <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(3)"> | |
Tickets <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(4)"> | |
Ticket Number <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(5)"> | |
Country <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(6)"> | |
Region <i class="fas fa-sort"></i> | |
</th> | |
<th onclick="sortTable(7)"> | |
Timestamp <i class="fas fa-sort"></i> | |
</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody id="ticketsTableBody"> | |
{% for ticket in tickets %} | |
<tr> | |
<td>{{ ticket.email }}</td> | |
<td>{{ ticket.phone }}</td> | |
<td>{{ ticket.name }}</td> | |
<td> | |
<span style="background: #f0f9ff; color: #0369a1; padding: 0.25rem 0.5rem; border-radius: 6px; font-size: 12px; font-weight: 500;">{{ ticket.tickets }}</span> | |
</td> | |
<td> | |
<span style="background: #f8fafc; color: #475569; padding: 0.25rem 0.5rem; border-radius: 4px; font-family: 'Monaco', monospace; font-size: 12px;">{{ ticket.ticket_number }}</span> | |
</td> | |
<td>{{ ticket.country }}</td> | |
<td>{{ ticket.region }}</td> | |
<td> | |
<span style="color: var(--text-secondary); font-size: 12px;">{{ ticket.timestamp }}</span> | |
</td> | |
<td> | |
<form method="POST" action="{{ url_for('delete_ticket', ticket_number=ticket.ticket_number) }}" | |
style="display: inline;" | |
onsubmit="return confirm('Are you sure you want to delete this ticket?')"> | |
<button type="submit" class="btn btn-outline-danger" style="padding: 0.375rem 0.5rem; font-size: 12px;"> | |
<i class="fas fa-trash" style="font-size: 11px;"></i> | |
</button> | |
</form> | |
</td> | |
</tr> | |
{% endfor %} | |
</tbody> | |
</table> | |
</div> | |
<div class="d-flex justify-content-between align-items-center mt-3"> | |
<div> | |
<small class="text-muted"> | |
Showing <span id="showingCount">{{ tickets|length }}</span> of {{ total_tickets }} records | |
</small> | |
</div> | |
<div> | |
<button class="btn btn-outline-primary btn-sm" onclick="exportVisible()"> | |
<i class="fas fa-download me-1"></i> | |
Export Visible | |
</button> | |
</div> | |
</div> | |
{% else %} | |
<div class="text-center py-5"> | |
<i class="fas fa-inbox fa-4x text-muted mb-3"></i> | |
<h4 class="text-muted">No Ticket Data Available</h4> | |
<p class="text-muted">No tickets have been submitted yet. Use the API endpoint to add ticket data.</p> | |
<a href="{{ url_for('index') }}" class="btn btn-primary"> | |
<i class="fas fa-plus me-1"></i> | |
View API Documentation | |
</a> | |
</div> | |
{% endif %} | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<footer class="bg-dark text-light mt-5 py-4"> | |
<div class="container text-center"> | |
<p class="mb-0"> | |
<i class="fas fa-shield-alt me-2"></i> | |
Secure Admin Dashboard - Last updated: <span id="lastUpdated"></span> | |
</p> | |
</div> | |
</footer> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> | |
<script src="{{ url_for('static', filename='js/main.js') }}"></script> | |
<script> | |
// Auto-refresh every 30 seconds | |
setInterval(function() { | |
document.getElementById('lastUpdated').textContent = new Date().toISOString().slice(0, 19).replace('T', ' '); | |
}, 30000); | |
</script> | |
</body> | |
</html> | |