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/bootstrap@5.3.0/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> | |
| <!-- Add this section after the ticket statistics cards --> | |
| <div class="row mb-4"> | |
| <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-clock me-2" style="color: var(--accent-color);"></i> | |
| Waiting List for Next Event | |
| <span class="badge bg-primary ms-2">{{ total_waiting }}</span> | |
| </h5> | |
| </div> | |
| <div class="card-body"> | |
| {% if waiting_list %} | |
| <div class="table-responsive"> | |
| <table class="table" id="waitingTable"> | |
| <thead> | |
| <tr> | |
| <th>Email</th> | |
| <th>Website</th> | |
| <th>Sign-up Date</th> | |
| <th>Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for entry in waiting_list %} | |
| <tr> | |
| <td>{{ entry.email }}</td> | |
| <td>{{ entry.website or 'Not provided' }}</td> | |
| <td> | |
| <span style="color: var(--text-secondary); font-size: 12px;">{{ entry.timestamp | |
| }}</span> | |
| </td> | |
| <td> | |
| <form method="POST" | |
| action="{{ url_for('delete_waiting_entry', entry_id=entry.id) }}" | |
| style="display: inline;" | |
| onsubmit="return confirm('Are you sure you want to remove this email from the waiting list?')"> | |
| <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> | |
| {% else %} | |
| <div class="text-center py-4"> | |
| <i class="fas fa-clock fa-3x text-muted mb-3"></i> | |
| <h5 class="text-muted">No one waiting yet</h5> | |
| <p class="text-muted">People who sign up for your next event will appear here.</p> | |
| </div> | |
| {% endif %} | |
| </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/bootstrap@5.3.0/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> |