format('M j, Y • g:i A');
} catch (Exception $e) {
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
}
function bind_named_values(PDOStatement $stmt, array $params): void {
foreach ($params as $name => $value) {
$stmt->bindValue($name, $value, is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR);
}
}
$message = admin_get_flash();
$pdo = db();
$records_per_page = 15;
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? max(1, (int) $_GET['page']) : 1;
$selected_webinar_id = isset($_GET['webinar_id']) && is_numeric($_GET['webinar_id']) ? max(0, (int) $_GET['webinar_id']) : 0;
$webinars = $pdo->query('SELECT id, title, scheduled_at FROM webinars ORDER BY scheduled_at DESC, id DESC')->fetchAll(PDO::FETCH_ASSOC);
$where_parts = ['a.deleted_at IS NULL'];
$params = [];
if ($selected_webinar_id > 0) {
$where_parts[] = 'a.webinar_id = :webinar_id';
$params[':webinar_id'] = $selected_webinar_id;
}
$where_sql = 'WHERE ' . implode(' AND ', $where_parts);
$count_stmt = $pdo->prepare("SELECT COUNT(*) FROM attendees a {$where_sql}");
bind_named_values($count_stmt, $params);
$count_stmt->execute();
$total_records = (int) $count_stmt->fetchColumn();
$total_pages = max(1, (int) ceil($total_records / $records_per_page));
if ($page > $total_pages) {
$page = $total_pages;
}
$offset = ($page - 1) * $records_per_page;
$today_stmt = $pdo->prepare("SELECT COUNT(*) FROM attendees a {$where_sql} AND DATE(a.created_at) = CURDATE()");
bind_named_values($today_stmt, $params);
$today_stmt->execute();
$today_count = (int) $today_stmt->fetchColumn();
$last7_stmt = $pdo->prepare("SELECT COUNT(*) FROM attendees a {$where_sql} AND a.created_at >= (NOW() - INTERVAL 7 DAY)");
bind_named_values($last7_stmt, $params);
$last7_stmt->execute();
$last_7_days_count = (int) $last7_stmt->fetchColumn();
$latest_stmt = $pdo->prepare("SELECT MAX(a.created_at) FROM attendees a {$where_sql}");
bind_named_values($latest_stmt, $params);
$latest_stmt->execute();
$latest_registration_at = $latest_stmt->fetchColumn();
$company_stmt = $pdo->prepare("SELECT COUNT(DISTINCT NULLIF(TRIM(a.company), '')) FROM attendees a {$where_sql}");
bind_named_values($company_stmt, $params);
$company_stmt->execute();
$unique_companies = (int) $company_stmt->fetchColumn();
$attendees_sql = "SELECT
a.id,
a.webinar_id,
a.first_name,
a.last_name,
a.email,
a.company,
a.how_did_you_hear,
a.timezone,
a.consented,
a.created_at,
w.title AS webinar_title,
w.scheduled_at AS webinar_scheduled_at
FROM attendees a
LEFT JOIN webinars w ON w.id = a.webinar_id
{$where_sql}
ORDER BY a.created_at DESC, a.id DESC
LIMIT :limit OFFSET :offset";
$attendees_stmt = $pdo->prepare($attendees_sql);
bind_named_values($attendees_stmt, $params);
$attendees_stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT);
$attendees_stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$attendees_stmt->execute();
$attendees = $attendees_stmt->fetchAll(PDO::FETCH_ASSOC);
$chart_stmt = $pdo->prepare("SELECT DATE(a.created_at) AS registration_day, COUNT(*) AS user_count
FROM attendees a
{$where_sql}
GROUP BY DATE(a.created_at)
ORDER BY DATE(a.created_at)");
bind_named_values($chart_stmt, $params);
$chart_stmt->execute();
$chart_data = $chart_stmt->fetchAll(PDO::FETCH_ASSOC);
$selected_webinar = null;
foreach ($webinars as $webinar) {
if ((int) $webinar['id'] === $selected_webinar_id) {
$selected_webinar = $webinar;
break;
}
}
$chart_labels = json_encode(array_column($chart_data, 'registration_day'));
$chart_values = json_encode(array_column($chart_data, 'user_count'));
$export_link = 'export_csv.php' . ($selected_webinar_id > 0 ? '?webinar_id=' . urlencode((string) $selected_webinar_id) : '');
?>
Admin Dashboard | Webinar Registrations
Webinar registrations dashboard
Welcome, . Review attendee data, edit registrations, export CSV, and monitor daily signup volume.
Total registrations
Active attendees.
Registered today
New signups recorded on format('M j, Y'); ?>.
Last 7 days
Rolling weekly registrations based on created_at.
Companies represented
Unique non-empty company names among active attendees.
Latest registration
Most recent active attendee creation timestamp.
Daily registrations
Chart based on attendee created_at, grouped by registration day.
| ID |
Attendee |
Email |
Company |
Source |
Timezone |
Webinar |
Registered at |
Actions |
| No attendees found for this filter yet. |
|
Consent:
|
|
|
|
|
|
|
|
1): ?>