Files
imf-sms-dashboard/web/attendees.html
T

306 lines
14 KiB
HTML

<!doctype html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Attendees — IMFestival SMS Dashboard</title>
<link href="assets/vendor/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" rel="stylesheet"
crossorigin="anonymous">
<link href="assets/css/dashboard.css" rel="stylesheet">
<link href="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone.css" rel="stylesheet">
<link href="https://cdn.datatables.net/2.3.8/css/dataTables.dataTables.min.css" rel="stylesheet">
</head>
<body>
<!-- ─── Mobile top bar ─── -->
<div class="topbar">
<span style="font-weight:600;font-size:0.95rem;">Attendees</span>
<span class="nav-username ms-auto" style="font-size:0.82rem;color:var(--text-muted);">User</span>
</div>
<!-- ─── Desktop sidebar ─── -->
<div class="sidebar">
<div class="brand">
<span style="margin: 0 auto;">IMFestival SMS Dashboard</span>
</div>
<nav class="d-flex flex-column gap-1 mt-2">
<a href="index.html" class="nav-link-custom">
<i class="fa-solid fa-house fa-fw"></i>
Home
</a>
<a href="attendees.html" class="nav-link-custom active">
<i class="fa-solid fa-users fa-fw"></i>
Attendees
</a>
<a href="templates.html" class="nav-link-custom">
<i class="fa-solid fa-comment-sms fa-fw"></i>
SMS Templates
</a>
<a href="schedule.html" class="nav-link-custom">
<i class="fa-solid fa-calendar-days fa-fw"></i>
Schedule
</a>
<a href="delivery-log.html" class="nav-link-custom">
<i class="fa-solid fa-list-check fa-fw"></i>
Delivery Log
</a>
</nav>
<div class="mt-auto pt-3" style="border-top:1px solid var(--border);">
<div class="nav-link-custom" style="cursor:default;">
<i class="fa-solid fa-circle-user fa-fw"></i>
<span class="nav-username" style="font-size:0.82rem;">User</span>
</div>
</div>
</div>
<!-- ─── Main content ─── -->
<div class="main">
<div class="main-inner">
<div id="sandboxBanner" class="alert alert-warning"
style="display:none;font-size:0.85rem;font-weight:600;text-align:center;">
<i class="fa-solid fa-flask me-1"></i> Sandbox mode is active — texts are tagged as a test and excluded
from
cost calculations.
</div>
<div class="page-header">
<h1>Attendees</h1>
<p>Upload a CSV file to import participants.</p>
</div>
<div class="card p-4 mb-4">
<h5 class="mb-3">Import CSV</h5>
<div class="mb-3 d-flex flex-wrap gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="importMode" id="modeClear" value="clear"
checked>
<label class="form-check-label" for="modeClear">
<strong>Clear &amp; Import</strong>
<span class="text-muted d-block" style="font-size:0.78rem;">Delete all existing attendees,
then import</span>
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="importMode" id="modeOverwrite"
value="overwrite">
<label class="form-check-label" for="modeOverwrite">
<strong>Overwrite Existing</strong>
<span class="text-muted d-block" style="font-size:0.78rem;">Update existing attendees, add
new ones</span>
</label>
</div>
</div>
<div id="csvDropzone" class="dropzone">
<div class="dz-message">
<i class="fa-solid fa-file-csv dz-icon"></i>
<span>Drop a CSV here or <strong>click to browse</strong></span>
<span class="text-muted" style="font-size:0.78rem;">Only .csv files accepted · Max 5 MB</span>
</div>
</div>
<div id="confirmUploadBar" class="d-none mt-2 d-flex align-items-center gap-2">
<span id="confirmUploadFilename" class="text-muted" style="font-size:0.85rem;"></span>
<button class="btn btn-sm btn-primary" onclick="confirmUpload()">
<i class="fa-solid fa-upload fa-fw"></i> Confirm Upload
</button>
<button class="btn btn-sm btn-secondary" onclick="cancelUpload()">
Cancel
</button>
</div>
<div id="importResult" class="mt-2" style="font-size:0.82rem;"></div>
</div>
<!-- ─── Rejected records (shown after import if rows were skipped) ─── -->
<div id="rejectedSection" class="card p-4 mb-4 d-none">
<div class="d-flex align-items-center justify-content-between mb-3 flex-wrap gap-2">
<h5 class="mb-0">
<i class="fa-solid fa-circle-exclamation fa-fw me-1" style="color:#facc15;"></i>
Rejected Records <span id="rejectedCount" class="badge ms-1"
style="background:rgba(250,204,21,0.15);color:#facc15;font-size:0.75rem;"></span>
</h5>
<div class="d-flex gap-2 flex-wrap align-items-center">
<div class="btn-group btn-group-sm" id="rejectedFilter" role="group"
aria-label="Filter rejected records">
<button type="button" class="btn btn-secondary" data-filter="all">All</button>
<button type="button" class="btn btn-secondary" data-filter="not_attending">Not
Attending</button>
<button type="button" class="btn btn-secondary active" data-filter="no_mobile">Missing
Mobile</button>
</div>
<button class="btn btn-sm btn-secondary" onclick="dismissAllRejected()">
<i class="fa-solid fa-xmark fa-fw"></i> Dismiss All
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-sm mb-0">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Mobile (raw)</th>
<th>Status</th>
<th>Reason</th>
<th></th>
</tr>
</thead>
<tbody id="rejectedBody">
<tr>
<td colspan="6" class="text-muted text-center py-3">No rejected records.</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card p-4">
<div class="d-flex align-items-center justify-content-between mb-3">
<h5 class="mb-0">Participants</h5>
<div class="d-flex align-items-center gap-3">
<span class="text-muted" id="attendeeCount" style="font-size:0.82rem;"></span>
<button class="btn btn-sm btn-danger" id="purgeBtn" onclick="purgeAttendees()">
<i class="fa-solid fa-trash-can fa-fw"></i> Purge All
</button>
</div>
</div>
<div class="table-responsive">
<table class="table table-striped mb-0" id="attendeesTable">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Mobile</th>
<th>Workshop</th>
<th>Time</th>
<th></th>
</tr>
</thead>
<tbody id="attendeeBody">
<tr>
<td colspan="6" class="text-muted text-center py-4">Loading…</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
crossorigin="anonymous"></script>
<script src="assets/js/nav.js"></script>
<script src="assets/js/app.js"></script>
<script src="https://unpkg.com/dropzone@6.0.0-beta.1/dist/dropzone-min.js"></script>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"
integrity="sha256-OaVG6prZf4v69dPg6PhVattBXkcOWQB62pdZ3ORyrao=" crossorigin="anonymous"></script>
<script src="https://cdn.datatables.net/2.3.8/js/dataTables.min.js"></script>
<!-- ─── Edit attendee modal ─── -->
<div class="modal fade" id="editModal" tabindex="-1" aria-labelledby="editModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editModalLabel">
<i class="fa-solid fa-pen fa-fw me-2"></i>Edit Attendee
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row g-3">
<div class="col-6">
<label class="form-label">First Name</label>
<input type="text" class="form-control" id="editFirstName">
</div>
<div class="col-6">
<label class="form-label">Last Name</label>
<input type="text" class="form-control" id="editLastName">
</div>
<div class="col-12">
<label class="form-label">Email</label>
<input type="email" class="form-control" id="editEmail">
</div>
<div class="col-12">
<label class="form-label">Mobile Number</label>
<input type="text" class="form-control" id="editMobile">
</div>
<div class="col-12">
<label class="form-label">Workshop</label>
<select class="form-select" id="editWorkshop">
<option value="">— No workshop —</option>
</select>
</div>
</div>
<div id="editError" class="text-danger mt-3" style="font-size:0.82rem;"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="editSaveBtn" onclick="saveEdit()">
<i class="fa-solid fa-floppy-disk fa-fw"></i> Save
</button>
</div>
</div>
</div>
</div>
<!-- ─── Duplicate resolution modal ─── -->
<div class="modal fade" id="dupModal" tabindex="-1" data-bs-backdrop="static" aria-labelledby="dupModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="dupModalLabel">
<i class="fa-solid fa-triangle-exclamation fa-fw me-2 text-warning"></i>Duplicate Mobile Numbers
</h5>
</div>
<div class="modal-body">
<p class="text-muted mb-3" style="font-size:0.85rem;">The following mobile numbers appear more than once in your CSV. Choose which entry to import for each:</p>
<div id="dupGroups"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="cancelDuplicates()">Cancel</button>
<button type="button" class="btn btn-primary" id="dupConfirmBtn" onclick="confirmDuplicates()">
<i class="fa-solid fa-check fa-fw"></i> Confirm Import
</button>
</div>
</div>
</div>
</div>
<nav class="bottom-nav" aria-label="Navigation">
<a href="index.html" class="bottom-nav-item">
<i class="fa-solid fa-house"></i>
<span>Home</span>
</a>
<a href="attendees.html" class="bottom-nav-item active">
<i class="fa-solid fa-users"></i>
<span>Attendees</span>
</a>
<a href="templates.html" class="bottom-nav-item">
<i class="fa-solid fa-comment-sms"></i>
<span>Templates</span>
</a>
<a href="schedule.html" class="bottom-nav-item">
<i class="fa-solid fa-calendar-days"></i>
<span>Schedule</span>
</a>
<a href="delivery-log.html" class="bottom-nav-item">
<i class="fa-solid fa-list-check"></i>
<span>Log</span>
</a>
<a href="admin.html" class="bottom-nav-item admin-nav-link d-none">
<i class="fa-solid fa-user-shield"></i>
<span>Admin</span>
</a>
</nav>
</body>
</html>