2
This commit is contained in:
parent
dbf75ca780
commit
b7c23d9356
117
admin.php
Normal file
117
admin.php
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Handle logout
|
||||||
|
if (isset($_GET['logout'])) {
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the user is not logged in as admin, redirect to the login page.
|
||||||
|
if (!isset($_SESSION['user']) || $_SESSION['user'] !== 'admin') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Data Fetching ---
|
||||||
|
require_once 'db/config.php';
|
||||||
|
$attendees = [];
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
// Updated to select first_name instead of name
|
||||||
|
$stmt = $pdo->query('SELECT a.id, w.title AS webinar_title, a.first_name, a.email, a.consented, a.created_at, a.timezone, a.utm_source, a.utm_medium, a.utm_campaign, a.referrer, a.gclid, a.fbclid FROM attendees a JOIN webinars w ON a.webinar_id = w.id ORDER BY a.created_at DESC');
|
||||||
|
$attendees = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Could not connect to the database: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Admin Board</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #121212;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1400px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.table-dark {
|
||||||
|
--bs-table-bg: #212529;
|
||||||
|
border-color: #373b3e;
|
||||||
|
}
|
||||||
|
.logout-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<a href="admin.php?logout=1" class="btn btn-secondary logout-btn">Logout</a>
|
||||||
|
<h1 class="mb-4 text-center">Webinar Attendees</h1>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-dark table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Webinar</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Consented</th>
|
||||||
|
<th>Registered At</th>
|
||||||
|
<th>Timezone</th>
|
||||||
|
<th>UTM Source</th>
|
||||||
|
<th>UTM Medium</th>
|
||||||
|
<th>UTM Campaign</th>
|
||||||
|
<th>Referrer</th>
|
||||||
|
<th>GCLID</th>
|
||||||
|
<th>FBCLID</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($attendees)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="13" class="text-center">No attendees yet.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($attendees as $attendee): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($attendee['id']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['webinar_title']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['first_name']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['email']) ?></td>
|
||||||
|
<td><?= $attendee['consented'] ? 'Yes' : 'No' ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['created_at']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['timezone']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['utm_source']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['utm_medium']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['utm_campaign']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['referrer']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['gclid']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($attendee['fbclid']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
29
api/pexels.php
Normal file
29
api/pexels.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require_once __DIR__.'/../includes/pexels.php';
|
||||||
|
$qs = isset($_GET['queries']) ? explode(',', $_GET['queries']) : ['professional portrait','man smiling','woman smiling'];
|
||||||
|
$out = [];
|
||||||
|
foreach ($qs as $q) {
|
||||||
|
$u = 'https://api.pexels.com/v1/search?query=' . urlencode(trim($q)) . '&orientation=square&per_page=1&page=1';
|
||||||
|
$d = pexels_get($u);
|
||||||
|
if ($d && !empty($d['photos'])) {
|
||||||
|
$p = $d['photos'][0];
|
||||||
|
$src = $p['src']['original'] ?? null;
|
||||||
|
$dest = __DIR__.'/../assets/images/pexels/'.$p['id'].'.jpg';
|
||||||
|
if ($src) download_to($src, $dest);
|
||||||
|
$out[] = [
|
||||||
|
'src' => 'assets/images/pexels/'.$p['id'].'.jpg',
|
||||||
|
'photographer' => $p['photographer'] ?? 'Unknown',
|
||||||
|
'photographer_url' => $p['photographer_url'] ?? '',
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// Fallback: Picsum
|
||||||
|
$out[] = [
|
||||||
|
'src' => 'https://picsum.photos/600',
|
||||||
|
'photographer' => 'Random Picsum',
|
||||||
|
'photographer_url' => 'https://picsum.photos/'
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo json_encode($out);
|
||||||
|
?>
|
||||||
BIN
assets/images/pexels/22601971.jpg
Normal file
BIN
assets/images/pexels/22601971.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/images/pexels/34238049.jpg
Normal file
BIN
assets/images/pexels/34238049.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 470 KiB |
BIN
assets/images/pexels/34285016.jpg
Normal file
BIN
assets/images/pexels/34285016.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 762 KiB |
BIN
assets/pasted-20251016-160256-cf9071d4.jpg
Normal file
BIN
assets/pasted-20251016-160256-cf9071d4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/pasted-20251016-161033-a327c220.png
Normal file
BIN
assets/pasted-20251016-161033-a327c220.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 200 KiB |
BIN
assets/vm-shot-2025-10-16T16-02-18-334Z.jpg
Normal file
BIN
assets/vm-shot-2025-10-16T16-02-18-334Z.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
126
dashboard.php
Normal file
126
dashboard.php
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attendee = null;
|
||||||
|
$webinar = null;
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
$user_id = $_SESSION['user_id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch attendee details
|
||||||
|
$stmt = db()->prepare("SELECT * FROM attendees WHERE id = ?");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$attendee = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($attendee) {
|
||||||
|
// Fetch webinar details
|
||||||
|
$stmt = db()->prepare("SELECT * FROM webinars WHERE id = ?");
|
||||||
|
$stmt->execute([$attendee['webinar_id']]);
|
||||||
|
$webinar = $stmt->fetch();
|
||||||
|
} else {
|
||||||
|
$error = 'Could not find your registration details.';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
$error = 'Database error. Please try again later.';
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Your Webinar Dashboard</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #1a202c;
|
||||||
|
color: #e2e8f0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.dashboard-box {
|
||||||
|
background-color: #2d3748;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 2rem;
|
||||||
|
border: 1px solid #4a5568;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.webinar-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #f6e05e;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.webinar-date {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.join-button {
|
||||||
|
display: inline-block;
|
||||||
|
background-color: #f6e05e;
|
||||||
|
color: #1a202c;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.join-button:hover {
|
||||||
|
background-color: #f6d32d;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background-color: #c53030;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="dashboard-box">
|
||||||
|
<h1>Your Dashboard</h1>
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="message"><?= htmlspecialchars($error) ?></div>
|
||||||
|
<?php elseif ($attendee && $webinar): ?>
|
||||||
|
<p>Hello, <strong><?= htmlspecialchars($attendee['first_name']) ?></strong>!</p>
|
||||||
|
<p>You are registered for the following webinar:</p>
|
||||||
|
<div class="webinar-title"><?= htmlspecialchars($webinar['title']) ?></div>
|
||||||
|
<div class="webinar-date">
|
||||||
|
<?php
|
||||||
|
$date = new DateTime($webinar['scheduled_at']);
|
||||||
|
echo $date->format('l, F j, Y \a\t g:i A T');
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<a href="join.php?id=<?= htmlspecialchars($attendee['id']) ?>" class="join-button">Join Webinar</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="message">Could not find your registration details.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<footer style="margin-top: 2rem; font-size: 0.9rem; color: #a0aec0;">
|
||||||
|
<a href="index.php" style="color: #a0aec0; text-decoration: none;">← Back to Home</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,26 +1,49 @@
|
|||||||
|
|
||||||
<?php
|
<?php
|
||||||
require_once 'config.php';
|
require_once 'config.php';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Connect without specifying a database
|
// Connect without specifying a database to ensure the DB exists
|
||||||
$pdo = new PDO('mysql:host='.DB_HOST, DB_USER, DB_PASS, [
|
$pdo = new PDO('mysql:host='.DB_HOST, DB_USER, DB_PASS, [
|
||||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
]);
|
]);
|
||||||
$pdo->exec('CREATE DATABASE IF NOT EXISTS '.DB_NAME);
|
$pdo->exec('CREATE DATABASE IF NOT EXISTS '.DB_NAME);
|
||||||
echo "Database created or already exists." . PHP_EOL;
|
|
||||||
|
|
||||||
// Now connect to the specific database
|
// Now connect to the specific database
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$migrations = glob(__DIR__ . '/migrations/*.sql');
|
|
||||||
sort($migrations);
|
|
||||||
|
|
||||||
foreach ($migrations as $migration) {
|
// 1. Create migrations table if it doesn't exist
|
||||||
$sql = file_get_contents($migration);
|
$pdo->exec('CREATE TABLE IF NOT EXISTS migrations (
|
||||||
$pdo->exec($sql);
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
echo "Executed migration: $migration" . PHP_EOL;
|
migration VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)');
|
||||||
|
|
||||||
|
// 2. Get all migration files
|
||||||
|
$migration_files = glob(__DIR__ . '/migrations/*.sql');
|
||||||
|
sort($migration_files);
|
||||||
|
|
||||||
|
// 3. Get already executed migrations
|
||||||
|
$executed_migrations_stmt = $pdo->query('SELECT migration FROM migrations');
|
||||||
|
$executed_migrations = $executed_migrations_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
foreach ($migration_files as $file) {
|
||||||
|
$migration_name = basename($file);
|
||||||
|
|
||||||
|
// 4. If migration has not been run, execute it
|
||||||
|
if (!in_array($migration_name, $executed_migrations)) {
|
||||||
|
$sql = file_get_contents($file);
|
||||||
|
$pdo->exec($sql);
|
||||||
|
|
||||||
|
// 5. Record the migration
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO migrations (migration) VALUES (?)');
|
||||||
|
$stmt->execute([$migration_name]);
|
||||||
|
|
||||||
|
echo "Executed migration: $migration_name" . PHP_EOL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo "All migrations have been run." . PHP_EOL;
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
die("Database migration failed: " . $e->getMessage());
|
die("Database migration failed: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
10
db/migrations/004_add_tracking_to_attendees.sql
Normal file
10
db/migrations/004_add_tracking_to_attendees.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
ALTER TABLE attendees
|
||||||
|
ADD COLUMN timezone VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN utm_source VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN utm_medium VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN utm_campaign VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN utm_term VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN utm_content VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN referrer VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN gclid VARCHAR(255) DEFAULT NULL,
|
||||||
|
ADD COLUMN fbclid VARCHAR(255) DEFAULT NULL;
|
||||||
1
db/migrations/005_add_password_to_attendees.sql
Normal file
1
db/migrations/005_add_password_to_attendees.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `attendees` ADD `password` VARCHAR(255) NOT NULL;
|
||||||
26
includes/pexels.php
Normal file
26
includes/pexels.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
function pexels_key() {
|
||||||
|
$k = getenv('PEXELS_KEY');
|
||||||
|
return $k && strlen($k) > 0 ? $k : 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18';
|
||||||
|
}
|
||||||
|
function pexels_get($url) {
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_URL => $url,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HTTPHEADER => [ 'Authorization: '. pexels_key() ],
|
||||||
|
CURLOPT_TIMEOUT => 15,
|
||||||
|
]);
|
||||||
|
$resp = curl_exec($ch);
|
||||||
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
if ($code >= 200 && $code < 300 && $resp) return json_decode($resp, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function download_to($srcUrl, $destPath) {
|
||||||
|
$data = file_get_contents($srcUrl);
|
||||||
|
if ($data === false) return false;
|
||||||
|
if (!is_dir(dirname($destPath))) mkdir(dirname($destPath), 0775, true);
|
||||||
|
return file_put_contents($destPath, $data) !== false;
|
||||||
|
}
|
||||||
|
?>
|
||||||
33
index.php
33
index.php
@ -1,9 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$webinar = null;
|
||||||
|
try {
|
||||||
|
// Fetch the first upcoming webinar
|
||||||
|
$stmt = db()->prepare("SELECT * FROM webinars WHERE scheduled_at > NOW() ORDER BY scheduled_at ASC LIMIT 1");
|
||||||
|
$stmt->execute();
|
||||||
|
$webinar = $stmt->fetch();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Silently fail for now
|
||||||
|
}
|
||||||
|
|
||||||
|
$webinar_id = $webinar ? $webinar['id'] : 1; // Fallback to 1 if no webinar is found
|
||||||
|
$webinar_title = $webinar ? $webinar['title'] : 'Professional Vibe-Coding Webinar';
|
||||||
|
$webinar_date_str = $webinar ? (new DateTime($webinar['scheduled_at']))->format('l, F j \\ • \\ At g.i A T') : 'Monday, November 3 • At 6.00 PM PST';
|
||||||
|
|
||||||
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Professional Vibe-Coding Webinar</title>
|
<title><?= htmlspecialchars($webinar_title) ?></title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
@ -71,8 +89,8 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 style="font-size: 2.5rem; font-weight: bold; color: #f6e05e; margin-bottom: 0.5rem;">Professional vibe-coding webinar</h1>
|
<h1 style="font-size: 2.5rem; font-weight: bold; color: #f6e05e; margin-bottom: 0.5rem;">Professional Vibe-Coding Webinar by Flatlogic</h1>
|
||||||
<p style="font-size: 1.25rem; color: #a0aec0; margin-bottom: 2rem;">by Flatlogic</p>
|
<p style="font-size: 1.25rem; color: #a0aec0; margin-bottom: 2rem;">Professional Vibe‑Coding: the fastest way to go from an idea to a working app you own, running on your server, with your database, using real frameworks.</p>
|
||||||
<div class="schedule-block">
|
<div class="schedule-block">
|
||||||
<h2>Schedule</h2>
|
<h2>Schedule</h2>
|
||||||
<ul>
|
<ul>
|
||||||
@ -81,13 +99,16 @@
|
|||||||
<li>• Q&A</li>
|
<li>• Q&A</li>
|
||||||
</ul>
|
</ul>
|
||||||
<div class="date-time">
|
<div class="date-time">
|
||||||
Monday, November 3 • At 6.00 PM PST
|
<?= htmlspecialchars($webinar_date_str) ?>
|
||||||
</div>
|
</div>
|
||||||
<a href="register.php" class="register-button">Register Now →</a>
|
<a href="register.php?webinar_id=<?= $webinar_id ?>" class="register-button">Register Now →</a>
|
||||||
<div class="byline">
|
<div class="byline">
|
||||||
By: Philip Daineka, Alex Rubanau, Alexey Vertel
|
By: Philip Daineka, Alex Rubanau, Alexey Vertel
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<header style="position: absolute; top: 1rem; right: 1rem;">
|
||||||
|
<a href="login.php" style="color: #1a202c; text-decoration: none; background-color: #f6e05e; padding: 0.75rem 1.5rem; border-radius: 0.375rem; font-weight: bold; font-size: 1.125rem;">Login</a>
|
||||||
|
</header>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
78
join.php
Normal file
78
join.php
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$attendee_id_encoded = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_STRING);
|
||||||
|
if (!$attendee_id_encoded) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo "Invalid link.";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attendee_id = base64_decode($attendee_id_encoded);
|
||||||
|
|
||||||
|
$attendee = null;
|
||||||
|
$webinar = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM attendees WHERE id = ?");
|
||||||
|
$stmt->execute([$attendee_id]);
|
||||||
|
$attendee = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($attendee) {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM webinars WHERE id = ?");
|
||||||
|
$stmt->execute([$attendee['webinar_id']]);
|
||||||
|
$webinar = $stmt->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Log error
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$attendee || !$webinar) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo "Registration not found.";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, just a welcome message. In a real scenario, this would redirect to the webinar platform.
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Welcome to the Webinar</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
|
background-color: #1a202c;
|
||||||
|
color: #e2e8f0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #f6e05e;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>Welcome, <?= htmlspecialchars($attendee['name']) ?>!</h1>
|
||||||
|
<p>You are now joining the webinar: <strong><?= htmlspecialchars($webinar['title']) ?></strong></p>
|
||||||
|
<p>The webinar is scheduled for: <strong><?= (new DateTime($webinar['scheduled_at']))->format('l, F j, Y \a\t g:i A T') ?></strong></p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
199
login.php
Normal file
199
login.php
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
require_once 'mail/MailService.php';
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['email'])) {
|
||||||
|
$email = trim($_POST['email']);
|
||||||
|
$password = isset($_POST['password']) ? $_POST['password'] : '';
|
||||||
|
|
||||||
|
// Admin Login
|
||||||
|
if ($email === 'admin' && $password === 'password') {
|
||||||
|
$_SESSION['user'] = 'admin';
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Participant Login
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT * FROM attendees WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$attendee = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($attendee && password_verify($password, $attendee['password'])) {
|
||||||
|
$_SESSION['user_id'] = $attendee['id'];
|
||||||
|
header('Location: dashboard.php');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$error = 'Invalid email or password. Would you like to <a href="register.php" style="color: #ffdd57;">register for the webinar</a>?';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log($e->getMessage());
|
||||||
|
$error = 'A database error occurred. Please try again later.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - Webinar Platform</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary-color: #4a90e2;
|
||||||
|
--background-color: #f4f7f6;
|
||||||
|
--card-background: #ffffff;
|
||||||
|
--text-color: #333;
|
||||||
|
--input-border: #e0e0e0;
|
||||||
|
--input-focus-border: #4a90e2;
|
||||||
|
--error-color: #d9534f;
|
||||||
|
--success-color: #5cb85c;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
.login-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.login-card {
|
||||||
|
background-color: var(--card-background);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 2.5rem;
|
||||||
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.subtitle {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
color: #777;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
input[type="email"], input[type="password"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
input[type="email"]:focus, input[type="password"]:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--input-focus-border);
|
||||||
|
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
|
||||||
|
}
|
||||||
|
.submit-button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 14px;
|
||||||
|
border: none;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
.submit-button:hover {
|
||||||
|
background-color: #357abd;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
background-color: #fbe9e7;
|
||||||
|
color: var(--error-color);
|
||||||
|
border: 1px solid var(--error-color);
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
background-color: #e8f5e9;
|
||||||
|
color: var(--success-color);
|
||||||
|
border: 1px solid var(--success-color);
|
||||||
|
}
|
||||||
|
.footer-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.footer-link a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.footer-link a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="logo">WebinarPlatform</div>
|
||||||
|
<div class="login-card">
|
||||||
|
<h1>Welcome Back</h1>
|
||||||
|
<p class="subtitle">Login to access your dashboard</p>
|
||||||
|
|
||||||
|
<?php if ($error): ?><div class="message error"><?= $error ?></div><?php endif; ?>
|
||||||
|
<?php if ($success): ?><div class="message success"><?= $success ?></div><?php endif; ?>
|
||||||
|
|
||||||
|
<form method="POST" action="login.php">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email Address</label>
|
||||||
|
<input type="email" id="email" name="email" required value="<?= isset($_POST['email']) ? htmlspecialchars($_POST['email']) : '' ?>">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="submit-button">Login</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="footer-link">
|
||||||
|
Don't have an account? <a href="register.php">Register for the Webinar</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="footer-link" style="margin-top: 2rem;">
|
||||||
|
<a href="index.php">← Back to Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
445
register.php
445
register.php
@ -1,191 +1,306 @@
|
|||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
|
||||||
require_once 'db/config.php';
|
require_once 'db/config.php';
|
||||||
|
require_once 'mail/MailService.php';
|
||||||
|
|
||||||
$pdo = db();
|
// --- CONFIGURATION ---
|
||||||
$webinar_id = filter_input(INPUT_GET, 'webinar_id', FILTER_VALIDATE_INT);
|
$webinar_id = filter_input(INPUT_GET, 'webinar_id', FILTER_VALIDATE_INT);
|
||||||
$webinar = null;
|
$response = [];
|
||||||
$feedback = [];
|
|
||||||
|
|
||||||
// Fetch webinar details
|
// --- WEBINAR DETAILS ---
|
||||||
|
$webinar = null;
|
||||||
if ($webinar_id) {
|
if ($webinar_id) {
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare('SELECT id, title, scheduled_at FROM webinars WHERE id = ?');
|
$stmt = db()->prepare("SELECT * FROM webinars WHERE id = ?");
|
||||||
$stmt->execute([$webinar_id]);
|
$stmt->execute([$webinar_id]);
|
||||||
$webinar = $stmt->fetch(PDO::FETCH_ASSOC);
|
$webinar = $stmt->fetch();
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
error_log("Webinar fetch error: " . $e->getMessage());
|
// Log error, but don't show to user
|
||||||
// Don't expose error details to user
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle form submission
|
// --- FORM SUBMISSION (POST REQUEST) ---
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
$name = trim($_POST['name'] ?? '');
|
header('Content-Type: application/json');
|
||||||
$email = filter_var(trim($_POST['email'] ?? ''), FILTER_VALIDATE_EMAIL);
|
|
||||||
$submitted_webinar_id = filter_input(INPUT_POST, 'webinar_id', FILTER_VALIDATE_INT);
|
// --- DATA CAPTURE ---
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$first_name = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_STRING);
|
||||||
|
$password = filter_input(INPUT_POST, 'password', FILTER_UNSAFE_RAW);
|
||||||
|
$confirm_password = filter_input(INPUT_POST, 'confirm_password', FILTER_UNSAFE_RAW);
|
||||||
|
$consent = filter_input(INPUT_POST, 'consent', FILTER_VALIDATE_BOOLEAN);
|
||||||
|
$timezone = filter_input(INPUT_POST, 'timezone', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
|
// Tracking data
|
||||||
|
$utm_source = filter_input(INPUT_POST, 'utm_source', FILTER_SANITIZE_STRING);
|
||||||
|
$utm_medium = filter_input(INPUT_POST, 'utm_medium', FILTER_SANITIZE_STRING);
|
||||||
|
$utm_campaign = filter_input(INPUT_POST, 'utm_campaign', FILTER_SANITIZE_STRING);
|
||||||
|
$utm_term = filter_input(INPUT_POST, 'utm_term', FILTER_SANITIZE_STRING);
|
||||||
|
$utm_content = filter_input(INPUT_POST, 'utm_content', FILTER_SANITIZE_STRING);
|
||||||
|
$referrer = filter_input(INPUT_POST, 'referrer', FILTER_SANITIZE_STRING);
|
||||||
|
$gclid = filter_input(INPUT_POST, 'gclid', FILTER_SANITIZE_STRING);
|
||||||
|
$fbclid = filter_input(INPUT_POST, 'fbclid', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
if (!$name || !$email || !$submitted_webinar_id) {
|
|
||||||
$feedback = ['type' => 'error', 'message' => 'Please fill in all fields with valid information.'];
|
if (!$email || !$consent || !$webinar) {
|
||||||
} else {
|
$response['error'] = 'Please provide a valid email and agree to the terms.';
|
||||||
try {
|
echo json_encode($response);
|
||||||
$stmt = $pdo->prepare('INSERT INTO attendees (webinar_id, name, email) VALUES (?, ?, ?)');
|
exit;
|
||||||
$stmt->execute([$submitted_webinar_id, $name, $email]);
|
}
|
||||||
$feedback = ['type' => 'success', 'message' => 'Thank you for registering! A confirmation has been sent to your email.'];
|
|
||||||
// In a real app, you would trigger an email here.
|
if ($password !== $confirm_password) {
|
||||||
} catch (PDOException $e) {
|
$response['error'] = 'Passwords do not match.';
|
||||||
error_log("Registration error: " . $e->getMessage());
|
echo json_encode($response);
|
||||||
if ($e->errorInfo[1] == 1062) { // Duplicate entry
|
exit;
|
||||||
$feedback = ['type' => 'error', 'message' => 'This email address has already been registered for this webinar.'];
|
}
|
||||||
} else {
|
|
||||||
$feedback = ['type' => 'error', 'message' => 'An error occurred during registration. Please try again.'];
|
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||||||
}
|
|
||||||
|
try {
|
||||||
|
// Check for duplicates
|
||||||
|
$stmt = db()->prepare("SELECT id FROM attendees WHERE webinar_id = ? AND email = ?");
|
||||||
|
$stmt->execute([$webinar_id, $email]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
$response['error'] = 'You are already registered for this webinar.';
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert attendee
|
||||||
|
$sql = "INSERT INTO attendees (webinar_id, name, email, password, timezone, utm_source, utm_medium, utm_campaign, utm_term, utm_content, referrer, gclid, fbclid)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$webinar_id, $first_name, $email, $password_hash, $timezone, $utm_source, $utm_medium, $utm_campaign, $utm_term, $utm_content, $referrer, $gclid, $fbclid]);
|
||||||
|
$attendee_id = db()->lastInsertId();
|
||||||
|
|
||||||
|
// --- SEND CONFIRMATION EMAIL ---
|
||||||
|
$webinar_date = new DateTime($webinar['scheduled_at']);
|
||||||
|
$subject = "Confirmation: You're Registered for " . $webinar['title'];
|
||||||
|
|
||||||
|
$body_html = "<h1>You're in!</h1>"
|
||||||
|
. "<p>Thanks for registering for our webinar: <strong>{$webinar['title']}</strong>.</p>"
|
||||||
|
. "<p>It will take place on <strong>" . $webinar_date->format('l, F j, Y \a\t g:i A T') . "</strong>.</p>"
|
||||||
|
. "<p>You can now log in to your dashboard to see the details.</p>";
|
||||||
|
|
||||||
|
MailService::sendMail($email, $subject, $body_html);
|
||||||
|
|
||||||
|
$response['success'] = true;
|
||||||
|
$response['webinar_title'] = $webinar['title'];
|
||||||
|
$response['webinar_date_utc'] = $webinar['scheduled_at'];
|
||||||
|
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// Log error
|
||||||
|
$response['error'] = 'A database error occurred. Please try again later.';
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Log error
|
||||||
|
$response['error'] = 'An error occurred: ' . $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
echo json_encode($response);
|
||||||
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- RENDER PAGE (GET REQUEST) ---
|
||||||
|
if (!$webinar) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo "Webinar not found.";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Register for Webinar</title>
|
<title>Register for <?= htmlspecialchars($webinar['title']) ?></title>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<style>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #1a202c; color: #e2e8f0; margin: 0; padding: 2rem; }
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
.container { max-width: 900px; margin: auto; display: grid; grid-template-columns: 1fr 1fr; gap: 4rem; align-items: start; }
|
||||||
<style>
|
.left-pane h1 { font-size: 2.5rem; color: #f6e05e; margin-bottom: 1rem; }
|
||||||
:root {
|
.left-pane .date-time { font-size: 1.25rem; font-weight: bold; margin-bottom: 1.5rem; }
|
||||||
--bg-color: #1A2035;
|
.left-pane .value-prop { font-size: 1.1rem; color: #a0aec0; line-height: 1.6; }
|
||||||
--primary-color: #F2A900;
|
.speakers { margin-top: 2rem; }
|
||||||
--text-color: #FFFFFF;
|
.speakers h3 { font-size: 1.2rem; color: #cbd5e0; margin-bottom: 1rem; }
|
||||||
--card-bg: #2A3045;
|
.speaker { display: flex; align-items: center; margin-bottom: 1rem; }
|
||||||
--error-color: #E57373;
|
.speaker img { width: 50px; height: 50px; border-radius: 50%; margin-right: 1rem; background-color: #4a5568; }
|
||||||
--success-color: #81C784;
|
.form-card { background-color: #2d3748; border-radius: 0.5rem; padding: 2rem; border: 1px solid #4a5568; }
|
||||||
}
|
.form-card h2 { margin-top: 0; font-size: 1.5rem; color: #f6e05e; text-align: center; }
|
||||||
body {
|
.form-group { margin-bottom: 1.5rem; }
|
||||||
margin: 0;
|
label { display: block; margin-bottom: 0.5rem; font-weight: bold; }
|
||||||
font-family: 'Poppins', sans-serif;
|
input[type="email"], input[type="text"] { width: 100%; padding: 0.75rem; background-color: #1a202c; border: 1px solid #4a5568; border-radius: 0.375rem; color: #e2e8f0; font-size: 1rem; box-sizing: border-box; }
|
||||||
background-color: var(--bg-color);
|
.consent-group { display: flex; align-items: start; gap: 0.75rem; }
|
||||||
color: var(--text-color);
|
.consent-group label { font-weight: normal; font-size: 0.9rem; color: #a0aec0; }
|
||||||
display: flex;
|
.microcopy { font-size: 0.8rem; color: #718096; margin-top: 0.5rem; }
|
||||||
justify-content: center;
|
.submit-btn { display: block; width: 100%; background-color: #f6e05e; color: #1a202c; padding: 0.85rem; border: none; border-radius: 0.375rem; font-weight: bold; font-size: 1.125rem; cursor: pointer; transition: background-color 0.2s; }
|
||||||
align-items: center;
|
.submit-btn:hover { background-color: #f6d32d; }
|
||||||
min-height: 100vh;
|
.submit-btn:disabled { background-color: #4a5568; cursor: not-allowed; }
|
||||||
}
|
#form-result { margin-top: 1.5rem; text-align: center; }
|
||||||
.container {
|
.success-message { background-color: #2d3748; border: 1px solid #4a5568; padding: 2rem; border-radius: 0.5rem; }
|
||||||
max-width: 500px;
|
.error-message { background-color: #c53030; padding: 1rem; border-radius: 0.375rem; }
|
||||||
width: 100%;
|
.calendar-buttons a, .copy-link-btn { display: inline-block; background-color: #4a5568; color: #e2e8f0; padding: 0.75rem 1.5rem; border-radius: 0.375rem; text-decoration: none; margin: 0.5rem; font-weight: bold; }
|
||||||
padding: 2rem;
|
@media (max-width: 768px) { .container { grid-template-columns: 1fr; } }
|
||||||
}
|
</style>
|
||||||
.form-container {
|
|
||||||
background: var(--card-bg);
|
|
||||||
padding: 2.5rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
.webinar-info {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
color: #E0E0E0;
|
|
||||||
}
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
input[type="text"], input[type="email"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.8rem;
|
|
||||||
border: 1px solid #4A5062;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: var(--bg-color);
|
|
||||||
color: var(--text-color);
|
|
||||||
font-family: 'Poppins', sans-serif;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.submit-btn {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0.9rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
color: var(--bg-color);
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
.submit-btn:hover {
|
|
||||||
background-color: #FFC107;
|
|
||||||
}
|
|
||||||
.feedback {
|
|
||||||
padding: 1rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.feedback.success {
|
|
||||||
background-color: var(--success-color);
|
|
||||||
color: #1B5E20;
|
|
||||||
}
|
|
||||||
.feedback.error {
|
|
||||||
background-color: var(--error-color);
|
|
||||||
color: #C62828;
|
|
||||||
}
|
|
||||||
.back-link {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 2rem;
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<header style="padding: 1rem;">
|
||||||
<div class="form-container">
|
<a href="index.php" style="color: #1a202c; text-decoration: none; background-color: #f6e05e; padding: 0.75rem 1.5rem; border-radius: 0.375rem; font-weight: bold; font-size: 1.125rem;">Back to Home</a>
|
||||||
<?php if ($feedback && $feedback['type'] === 'success'): ?>
|
</header>
|
||||||
<h1>Registration Successful!</h1>
|
<div class="container">
|
||||||
<div class="feedback success"><?= htmlspecialchars($feedback['message']) ?></div>
|
<div class="left-pane">
|
||||||
<a href="index.php" class="back-link">← Back to Webinars</a>
|
<h1><?= htmlspecialchars($webinar['title']) ?></h1>
|
||||||
<?php elseif ($webinar): ?>
|
<div class="date-time" id="local-date-time">Loading date...</div>
|
||||||
<h1>Register for Webinar</h1>
|
<p class="value-prop"><?= htmlspecialchars($webinar['description']) ?></p>
|
||||||
<p class="webinar-info">You are registering for: <strong><?= htmlspecialchars($webinar['title']) ?></strong></p>
|
<div class="speakers">
|
||||||
|
<h3>Speakers</h3>
|
||||||
<?php if ($feedback && $feedback['type'] === 'error'): ?>
|
<div class="speaker"><img src="assets/images/pexels/22601971.jpg" alt="Philip Daineka"> <span>Philip Daineka</span></div>
|
||||||
<div class="feedback error"><?= htmlspecialchars($feedback['message']) ?></div>
|
<div class="speaker"><img src="assets/images/pexels/34285016.jpg" alt="Alex Rubanau"> <span>Alex Rubanau</span></div>
|
||||||
<?php endif; ?>
|
<div class="speaker"><img src="assets/images/pexels/34238049.jpg" alt="Alexey Vertel"> <span>Alexey Vertel</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="register.php?webinar_id=<?= $webinar_id ?>" method="POST">
|
<div class="right-pane">
|
||||||
<input type="hidden" name="webinar_id" value="<?= $webinar_id ?>">
|
<div id="registration-form-container">
|
||||||
<div class="form-group">
|
<form id="registration-form" class="form-card" method="POST">
|
||||||
<label for="name">Full Name</label>
|
<h2>Register & Get Calendar Invite</h2>
|
||||||
<input type="text" id="name" name="name" required>
|
<div class="form-group">
|
||||||
</div>
|
<label for="email">Email (required)</label>
|
||||||
<div class="form-group">
|
<input type="email" id="email" name="email" required>
|
||||||
<label for="email">Email Address</label>
|
<p class="microcopy">We’ll send the join link & calendar invite.</p>
|
||||||
<input type="email" id="email" name="email" required>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<button type="submit" class="submit-btn">Register Now</button>
|
<label for="first_name">First name (optional)</label>
|
||||||
</form>
|
<input type="text" id="first_name" name="first_name">
|
||||||
<a href="index.php" class="back-link">← Cancel</a>
|
</div>
|
||||||
<?php else: ?>
|
<div class="form-group">
|
||||||
<h1>Webinar Not Found</h1>
|
<label for="password">Password</label>
|
||||||
<p style="text-align:center">The requested webinar could not be found.</p>
|
<input type="password" id="password" name="password" required>
|
||||||
<a href="index.php" class="back-link">← Back to Webinars</a>
|
</div>
|
||||||
<?php endif; ?>
|
<div class="form-group">
|
||||||
|
<label for="confirm_password">Confirm Password</label>
|
||||||
|
<input type="password" id="confirm_password" name="confirm_password" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group consent-group">
|
||||||
|
<input type="checkbox" id="consent" name="consent" required>
|
||||||
|
<label for="consent">I agree to receive info about this webinar.</label>
|
||||||
|
</div>
|
||||||
|
<p class="microcopy" style="margin-top:-1rem; margin-bottom: 1.5rem;">No spam. Unsubscribe anytime.</p>
|
||||||
|
|
||||||
|
<input type="hidden" name="timezone" id="timezone">
|
||||||
|
<input type="hidden" name="utm_source" id="utm_source">
|
||||||
|
<input type="hidden" name="utm_medium" id="utm_medium">
|
||||||
|
<input type="hidden" name="utm_campaign" id="utm_campaign">
|
||||||
|
<input type="hidden" name="utm_term" id="utm_term">
|
||||||
|
<input type="hidden" name="utm_content" id="utm_content">
|
||||||
|
<input type="hidden" name="referrer" id="referrer">
|
||||||
|
<input type="hidden" name="gclid" id="gclid">
|
||||||
|
<input type="hidden" name="fbclid" id="fbclid">
|
||||||
|
|
||||||
|
<button type="submit" class="submit-btn" id="submit-button">Register Now</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="form-result"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<script>
|
||||||
|
// --- TIMEZONE & DATE LOCALIZATION ---
|
||||||
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
|
document.getElementById('timezone').value = timezone;
|
||||||
|
|
||||||
|
const webinarDateUTC = '<?= $webinar["scheduled_at"] ?>';
|
||||||
|
const localDate = new Date(webinarDateUTC + 'Z'); // 'Z' for UTC
|
||||||
|
|
||||||
|
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||||
|
document.getElementById('local-date-time').textContent = localDate.toLocaleDateString(undefined, options);
|
||||||
|
|
||||||
|
// --- TRACKING PARAMS ---
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'gclid', 'fbclid'].forEach(param => {
|
||||||
|
if (urlParams.has(param)) {
|
||||||
|
document.getElementById(param).value = urlParams.get(param);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.getElementById('referrer').value = document.referrer;
|
||||||
|
|
||||||
|
|
||||||
|
// --- AJAX FORM SUBMISSION ---
|
||||||
|
const form = document.getElementById('registration-form');
|
||||||
|
const formContainer = document.getElementById('registration-form-container');
|
||||||
|
const formResult = document.getElementById('form-result');
|
||||||
|
const submitButton = document.getElementById('submit-button');
|
||||||
|
|
||||||
|
form.addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
submitButton.disabled = true;
|
||||||
|
submitButton.textContent = 'Processing...';
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
fetch(window.location.href, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
formContainer.style.display = 'none';
|
||||||
|
const webinarTitle = encodeURIComponent(data.webinar_title);
|
||||||
|
const webinarDate = new Date(data.webinar_date_utc + 'Z');
|
||||||
|
const startTime = webinarDate.toISOString().replace(/-|:|..*Z/g, '') + 'Z';
|
||||||
|
const endTime = new Date(webinarDate.getTime() + (60 * 60 * 1000)).toISOString().replace(/-|:|..*Z/g, '') + 'Z';
|
||||||
|
|
||||||
|
const googleLink = `https://www.google.com/calendar/render?action=TEMPLATE&text=${webinarTitle}&dates=${startTime.replace(/Z$/, '')}/${endTime.replace(/Z$/, '')}&details=Join+the+webinar!`;
|
||||||
|
|
||||||
|
const icsContent = [
|
||||||
|
'BEGIN:VCALENDAR',
|
||||||
|
'VERSION:2.0',
|
||||||
|
'BEGIN:VEVENT',
|
||||||
|
`URL:${window.location.href}`,
|
||||||
|
`DTSTART:${startTime.replace(/Z$/, '')}`,
|
||||||
|
`DTEND:${endTime.replace(/Z$/, '')}`,
|
||||||
|
`SUMMARY:${data.webinar_title}`,
|
||||||
|
'DESCRIPTION:Join the webinar!',
|
||||||
|
'END:VEVENT',
|
||||||
|
'END:VCALENDAR'
|
||||||
|
].join('\n');
|
||||||
|
const outlookLink = `data:text/calendar;charset=utf8,${encodeURIComponent(icsContent)}`;
|
||||||
|
|
||||||
|
formResult.innerHTML = `
|
||||||
|
<div class="success-message">
|
||||||
|
<h3 style="color: #f6e05e; font-size: 1.75rem;">You’re in for ${data.webinar_title}!</h3>
|
||||||
|
<p style="color: #cbd5e0; font-size: 1.1rem;">Check your email for your confirmation. You can now log in to see the details.</p>
|
||||||
|
<div class="calendar-buttons">
|
||||||
|
<a href="${googleLink}" target="_blank">Add to Google Calendar</a>
|
||||||
|
<a href="${outlookLink}" download="webinar.ics">Add to Outlook (ICS)</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
formResult.innerHTML = `<div class="error-message">${data.error}</div>`;
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.textContent = 'Register Now';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
formResult.innerHTML = `<div class="error-message">An unexpected error occurred.</div>`;
|
||||||
|
submitButton.disabled = false;
|
||||||
|
submitButton.textContent = 'Register Now';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- COPY LINK ---
|
||||||
|
formResult.addEventListener('click', function(e) {
|
||||||
|
if (e.target.classList.contains('copy-link-btn')) {
|
||||||
|
e.preventDefault();
|
||||||
|
const link = e.target.dataset.link;
|
||||||
|
navigator.clipboard.writeText(link).then(() => {
|
||||||
|
e.target.textContent = 'Copied!';
|
||||||
|
setTimeout(() => {
|
||||||
|
e.target.textContent = 'Copy Join Link';
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user