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, description, presenter, 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; } } $preview_webinar = $selected_webinar ?? ($webinars[0] ?? null); $preview_email_payload = null; $preview_scope_note = null; if ($preview_webinar) { $preview_email_payload = webinar_build_email_payload('there', $preview_webinar, true); $preview_scope_note = $selected_webinar ? 'This preview matches the correction email for the selected webinar.' : 'Preview shows the correction email template for the most recent webinar. When "All webinars" is selected, each attendee still receives the version for their own webinar.'; } $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.

Reset
Download CSV
View site Log out
Sends the corrected webinar time, Google Meet link, Google Calendar button, and .ics file to all active attendees.

Correction email preview

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.

Registered attendees

Showing active records for . Showing active records for all webinars, newest registrations first.

ID Attendee Email Company Source Timezone Webinar Registered at Actions
No attendees found for this filter yet.
Consent:
Edit
1): ?>