Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce9f0dfe67 | ||
|
|
883324698c | ||
|
|
487c883d7f | ||
|
|
551fb8d177 | ||
|
|
d553a4a914 | ||
|
|
c733efa8ca | ||
|
|
84ff927075 | ||
|
|
1b68b62fde | ||
|
|
d05f2381f7 | ||
|
|
8f1d678d64 | ||
|
|
5cc3f02c65 | ||
|
|
a2ff9876ac | ||
|
|
94ddfaeca6 | ||
|
|
2c7ebcace0 | ||
|
|
2c65f7a5df | ||
|
|
a1880c468c | ||
|
|
bd0c257d11 | ||
|
|
2e98c61f0c | ||
|
|
7b84a1fc13 | ||
|
|
ac0f115fb6 | ||
|
|
f400adcf7c | ||
|
|
a2b2e8ae3b | ||
|
|
f8e0cdfba4 | ||
|
|
0dc1ca6104 | ||
|
|
16c40428e5 | ||
|
|
b9f0c9ea5a | ||
|
|
d762bc8263 | ||
|
|
4ba12356e0 | ||
|
|
9a7532b7f7 | ||
|
|
b7c23d9356 | ||
|
|
dbf75ca780 |
186
admin.php
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php'; // Assuming you have a db connection setup
|
||||||
|
|
||||||
|
// Check if user is admin
|
||||||
|
if (!isset($_SESSION['user']) || $_SESSION['user'] !== 'admin') {
|
||||||
|
// If not admin, redirect to login page
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for messages
|
||||||
|
$message = '';
|
||||||
|
if (isset($_SESSION['message'])) {
|
||||||
|
$message = $_SESSION['message'];
|
||||||
|
unset($_SESSION['message']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Pagination settings
|
||||||
|
$records_per_page = 10;
|
||||||
|
$page = isset($_GET['page']) && is_numeric($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
|
$offset = ($page - 1) * $records_per_page;
|
||||||
|
|
||||||
|
// Get total number of records
|
||||||
|
$total_stmt = $pdo->query("SELECT COUNT(*) FROM attendees");
|
||||||
|
$total_records = $total_stmt->fetchColumn();
|
||||||
|
$total_pages = ceil($total_records / $records_per_page);
|
||||||
|
|
||||||
|
// Get records for the current page
|
||||||
|
$stmt = $pdo->prepare("SELECT id, first_name, last_name, email, created_at, how_did_you_hear, company FROM attendees ORDER BY first_name ASC, last_name ASC LIMIT :limit OFFSET :offset");
|
||||||
|
$stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT);
|
||||||
|
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
||||||
|
$stmt->execute();
|
||||||
|
$attendees = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Get data for the chart
|
||||||
|
$chart_stmt = $pdo->query("SELECT DATE(created_at) as registration_day, COUNT(*) as user_count FROM attendees GROUP BY registration_day ORDER BY registration_day");
|
||||||
|
$chart_data = $chart_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$chart_labels = json_encode(array_column($chart_data, 'registration_day'));
|
||||||
|
$chart_values = json_encode(array_column($chart_data, 'user_count'));
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Admin Dashboard</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-5">
|
||||||
|
<h2>Admin Dashboard</h2>
|
||||||
|
<p>Welcome, <?php echo htmlspecialchars($_SESSION['user'] ?? 'Admin'); ?>!</p>
|
||||||
|
|
||||||
|
<?php if ($message): ?>
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<?php echo htmlspecialchars($message); ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="mt-4 p-4 border rounded shadow-sm bg-light">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h3 class="h5 mb-0">Daily Registrations</h3>
|
||||||
|
<button id="downloadChartBtn" class="btn btn-sm btn-outline-primary">Save as Image</button>
|
||||||
|
</div>
|
||||||
|
<div style="max-width: 600px; margin: auto;">
|
||||||
|
<canvas id="registrationsChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<h3 class="mt-4">Registered Attendees</h3>
|
||||||
|
<a href="export_csv.php" class="btn btn-success">Download CSV</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>First Name</th>
|
||||||
|
<th>Last Name</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Source</th>
|
||||||
|
<th>Company</th>
|
||||||
|
<th>Registered At</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if (empty($attendees)): ?>
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center">No attendees found.</td>
|
||||||
|
</tr>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($attendees as $attendee): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['id']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['first_name']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['last_name']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['email']); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['how_did_you_hear'] ?? ''); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['company'] ?? ''); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($attendee['created_at']); ?></td>
|
||||||
|
<td>
|
||||||
|
<a href="edit_attendee.php?id=<?php echo $attendee['id']; ?>" class="btn btn-sm btn-primary">Edit</a>
|
||||||
|
<form action="delete_attendee.php" method="POST" style="display: inline-block;">
|
||||||
|
<input type="hidden" name="id" value="<?php echo $attendee['id']; ?>">
|
||||||
|
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Delete</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav aria-label="Page navigation">
|
||||||
|
<ul class="pagination justify-content-center">
|
||||||
|
<?php if ($page > 1): ?>
|
||||||
|
<li class="page-item"><a class="page-link" href="?page=<?php echo $page - 1; ?>">Previous</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
|
||||||
|
<li class="page-item <?php if ($i == $page) echo 'active'; ?>"><a class="page-link" href="?page=<?php echo $i; ?>"><?php echo $i; ?></a></li>
|
||||||
|
<?php endfor; ?>
|
||||||
|
|
||||||
|
<?php if ($page < $total_pages): ?>
|
||||||
|
<li class="page-item"><a class="page-link" href="?page=<?php echo $page + 1; ?>">Next</a></li>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const ctx = document.getElementById('registrationsChart');
|
||||||
|
const registrationsChart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: <?php echo $chart_labels; ?>,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Daily Registrations',
|
||||||
|
data: <?php echo $chart_values; ?>,
|
||||||
|
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||||||
|
borderColor: 'rgba(0, 123, 255, 1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
tension: 0.4,
|
||||||
|
fill: true
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
stepSize: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'User Registrations per Day'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('downloadChartBtn').addEventListener('click', function() {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = registrationsChart.toBase64Image();
|
||||||
|
link.download = 'daily-registrations-chart.png';
|
||||||
|
link.click();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
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);
|
||||||
|
?>
|
||||||
0
assets/images/flatlogic_logo.png
Normal file
BIN
assets/images/pexels/22601971.jpg
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/images/pexels/34238049.jpg
Normal file
|
After Width: | Height: | Size: 470 KiB |
BIN
assets/images/pexels/34285016.jpg
Normal file
|
After Width: | Height: | Size: 762 KiB |
BIN
assets/pasted-20251016-141336-dce056a4.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
assets/pasted-20251016-141746-e994507c.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/pasted-20251016-142345-48170932.png
Normal file
|
After Width: | Height: | Size: 501 KiB |
BIN
assets/pasted-20251016-160256-cf9071d4.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/pasted-20251016-161033-a327c220.png
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
assets/pasted-20251017-092558-6823fffc.png
Normal file
|
After Width: | Height: | Size: 601 KiB |
BIN
assets/pasted-20251017-093140-48a8b5ba.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
assets/pasted-20251017-093539-e74373a7.png
Normal file
|
After Width: | Height: | Size: 511 KiB |
BIN
assets/pasted-20251017-094703-ce814c6a.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
assets/pasted-20251017-095855-e8ae3c6d.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/pasted-20251017-100534-44f1cec7.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
assets/pasted-20251017-105229-1478f697.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/pasted-20251017-112741-3509bc32.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
assets/pasted-20251017-113238-94b9c787.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
16
assets/pasted-20251017-113531-8f1dec11.jpg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="755" height="113" viewBox="0 0 755 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M79.2858 44.5879C79.2858 41.8567 81.4998 39.6427 84.2309 39.6427H106.055C108.786 39.6427 111 41.8567 111 44.5879V58.4832C111 61.2143 108.786 63.4284 106.055 63.4284H84.2309C81.4998 63.4284 79.2858 61.2143 79.2858 58.4832V44.5879Z" fill="#02004E"/>
|
||||||
|
<path d="M0 4.94518C0 2.21404 2.21403 0 4.94518 0H106.055C108.786 0 111 2.21403 111 4.94518V22.8048C111 25.5359 108.786 27.75 106.055 27.75H4.94518C2.21404 27.75 0 25.5359 0 22.8048V4.94518Z" fill="#5C7EF1"/>
|
||||||
|
<path d="M0 80.2665C0 77.5353 2.21403 75.3213 4.94518 75.3213H22.8048C25.5359 75.3213 27.75 77.5353 27.75 80.2665V94.1618C27.75 96.8929 25.5359 99.107 22.8048 99.107H4.94518C2.21404 99.107 0 96.8929 0 94.1618V80.2665Z" fill="#8C9DFF"/>
|
||||||
|
<path d="M43.6071 80.2665C43.6071 77.5353 45.8212 75.3213 48.5523 75.3213H106.055C108.786 75.3213 111 77.5353 111 80.2665V94.1618C111 96.8929 108.786 99.107 106.055 99.107H48.5523C45.8212 99.107 43.6071 96.8929 43.6071 94.1618V80.2665Z" fill="#02004E"/>
|
||||||
|
<path d="M0 44.5879C0 41.8567 2.21403 39.6427 4.94518 39.6427H58.4833C61.2144 39.6427 63.4285 41.8567 63.4285 44.5879V58.4832C63.4285 61.2143 61.2144 63.4284 58.4833 63.4284H4.94518C2.21403 63.4284 0 61.2143 0 58.4832V44.5879Z" fill="#FFA70B"/>
|
||||||
|
<path d="M147.724 10.7838H198.315V25.0124H162.63V43.0804H197.637V57.3089H162.63V89.8314H147.724V10.7838Z" fill="#02004E"/>
|
||||||
|
<path d="M215.401 76.2804H235.05V24.3348H216.078V10.7838H249.278V76.2804H267.572V89.8314H215.401V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M321.133 81.7008C319.627 85.2391 317.519 87.7611 314.809 89.2667C312.099 90.6971 308.937 91.4123 305.323 91.4123C301.936 91.4123 298.736 90.7724 295.725 89.4926C292.789 88.2128 290.191 86.3307 287.933 83.8463C285.674 81.362 283.868 78.3506 282.512 74.8123C281.233 71.274 280.593 67.2463 280.593 62.7293V60.9225C280.593 56.4808 281.233 52.4908 282.512 48.9525C283.792 45.4142 285.524 42.4028 287.707 39.9185C289.89 37.4341 292.412 35.5521 295.273 34.2722C298.209 32.9171 301.333 32.2396 304.646 32.2396C308.636 32.2396 311.835 32.9171 314.244 34.2722C316.729 35.6273 318.686 37.7353 320.117 40.596H322.149V33.8205H336.378V72.8926C336.378 75.1511 337.394 76.2804 339.427 76.2804H341.572V89.8314H331.861C329.301 89.8314 327.193 89.0785 325.537 87.5729C323.956 86.0672 323.166 84.1098 323.166 81.7008H321.133ZM308.485 77.8613C312.626 77.8613 315.938 76.5062 318.423 73.796C320.907 71.0105 322.149 67.2464 322.149 62.5035V61.1484C322.149 56.4055 320.907 52.679 318.423 49.9688C315.938 47.1833 312.626 45.7906 308.485 45.7906C304.345 45.7906 301.032 47.1833 298.548 49.9688C296.064 52.679 294.821 56.4055 294.821 61.1484V62.5035C294.821 67.2464 296.064 71.0105 298.548 73.796C301.032 76.5062 304.345 77.8613 308.485 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M352.448 33.8205H369.047V10.7838H383.276V33.8205H403.49V47.3715H383.276V72.8926C383.276 75.1511 384.292 76.2804 386.325 76.2804H401.457V89.8314H377.743C375.183 89.8314 373.075 89.0032 371.419 87.347C369.838 85.6908 369.047 83.5828 369.047 81.0232V47.3715H352.448V33.8205Z" fill="#02004E"/>
|
||||||
|
<path d="M422.834 76.2804H442.483V24.3348H423.512V10.7838H456.712V76.2804H475.006V89.8314H422.834V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M547.199 62.5035C547.199 67.1711 546.409 71.3117 544.828 74.9253C543.247 78.4636 541.101 81.4749 538.391 83.9593C535.681 86.3683 532.556 88.2128 529.018 89.4926C525.555 90.7724 521.904 91.4123 518.064 91.4123C514.225 91.4123 510.536 90.7724 506.998 89.4926C503.535 88.2128 500.448 86.3683 497.738 83.9593C495.028 81.4749 492.882 78.4636 491.301 74.9253C489.72 71.3117 488.93 67.1711 488.93 62.5035V61.1484C488.93 56.5561 489.72 52.4908 491.301 48.9525C492.882 45.3389 495.028 42.2899 497.738 39.8056C500.448 37.3212 503.535 35.4391 506.998 34.1593C510.536 32.8795 514.225 32.2396 518.064 32.2396C521.904 32.2396 525.555 32.8795 529.018 34.1593C532.556 35.4391 535.681 37.3212 538.391 39.8056C541.101 42.2899 543.247 45.3389 544.828 48.9525C546.409 52.4908 547.199 56.5561 547.199 61.1484V62.5035ZM518.064 77.8613C520.097 77.8613 522.017 77.5225 523.824 76.845C525.63 76.1674 527.211 75.1887 528.566 73.9089C529.922 72.6291 530.976 71.0858 531.728 69.279C532.556 67.3969 532.971 65.289 532.971 62.9552V60.6967C532.971 58.3629 532.556 56.2926 531.728 54.4858C530.976 52.6037 529.922 51.0228 528.566 49.743C527.211 48.4632 525.63 47.4845 523.824 46.8069C522.017 46.1294 520.097 45.7906 518.064 45.7906C516.032 45.7906 514.112 46.1294 512.305 46.8069C510.498 47.4845 508.918 48.4632 507.562 49.743C506.207 51.0228 505.116 52.6037 504.288 54.4858C503.535 56.2926 503.158 58.3629 503.158 60.6967V62.9552C503.158 65.289 503.535 67.3969 504.288 69.279C505.116 71.0858 506.207 72.6291 507.562 73.9089C508.918 75.1887 510.498 76.1674 512.305 76.845C514.112 77.5225 516.032 77.8613 518.064 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M601.438 81.7008H599.405C598.803 82.9806 598.05 84.2227 597.146 85.4273C596.318 86.5565 595.189 87.5729 593.759 88.4763C592.404 89.3797 590.747 90.0948 588.79 90.6218C586.908 91.1488 584.612 91.4123 581.901 91.4123C578.438 91.4123 575.201 90.81 572.19 89.6055C569.254 88.3257 566.657 86.4812 564.398 84.0722C562.215 81.5878 560.483 78.5765 559.204 75.0382C557.999 71.4999 557.397 67.4346 557.397 62.8423V60.8096C557.397 56.2926 558.037 52.265 559.316 48.7266C560.672 45.1883 562.478 42.2146 564.737 39.8056C567.071 37.3212 569.743 35.4391 572.755 34.1593C575.841 32.8795 579.078 32.2396 582.466 32.2396C586.983 32.2396 590.559 33.1053 593.194 34.8369C595.829 36.4931 597.899 38.8645 599.405 41.9511H601.438V33.8205H615.666V103.608C615.666 106.168 614.838 108.276 613.182 109.932C611.601 111.588 609.53 112.416 606.971 112.416H569.254V98.8654H598.389C600.421 98.8654 601.438 97.7361 601.438 95.4776V81.7008ZM586.531 77.8613C590.973 77.8613 594.549 76.4686 597.259 73.6831C600.045 70.8223 601.438 67.0958 601.438 62.5035V61.1484C601.438 56.5561 600.045 52.8672 597.259 50.0817C594.549 47.221 590.973 45.7906 586.531 45.7906C582.09 45.7906 578.476 47.1833 575.691 49.9688C572.98 52.679 571.625 56.4055 571.625 61.1484V62.5035C571.625 67.2464 572.98 71.0105 575.691 73.796C578.476 76.5062 582.09 77.8613 586.531 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M631.623 76.2804H650.594V47.3715H632.978V33.8205H664.823V76.2804H681.084V89.8314H631.623V76.2804ZM668.324 17.2206C668.324 18.7262 668.022 20.1566 667.42 21.5117C666.893 22.7915 666.14 23.9208 665.162 24.8995C664.183 25.8029 663.016 26.5557 661.661 27.158C660.381 27.6849 659.026 27.9484 657.596 27.9484C656.09 27.9484 654.697 27.6849 653.417 27.158C652.138 26.5557 651.008 25.8029 650.03 24.8995C649.051 23.9208 648.261 22.7915 647.658 21.5117C647.131 20.1566 646.868 18.7262 646.868 17.2206C646.868 15.7149 647.131 14.3221 647.658 13.0423C648.261 11.6872 649.051 10.558 650.03 9.65458C651.008 8.67589 652.138 7.92306 653.417 7.39608C654.697 6.79381 656.09 6.49268 657.596 6.49268C659.026 6.49268 660.381 6.79381 661.661 7.39608C663.016 7.92306 664.183 8.67589 665.162 9.65458C666.14 10.558 666.893 11.6872 667.42 13.0423C668.022 14.3221 668.324 15.7149 668.324 17.2206Z" fill="#02004E"/>
|
||||||
|
<path d="M754.407 69.279C753.127 75.9039 750.078 81.2491 745.26 85.3144C740.517 89.3797 734.156 91.4123 726.176 91.4123C722.035 91.4123 718.158 90.7724 714.544 89.4926C711.006 88.2128 707.919 86.3683 705.284 83.9593C702.65 81.5502 700.579 78.5765 699.074 75.0382C697.568 71.4999 696.815 67.4722 696.815 62.9552V61.6001C696.815 57.0831 697.568 53.0178 699.074 49.4042C700.579 45.7906 702.65 42.704 705.284 40.1443C707.995 37.5847 711.119 35.6273 714.657 34.2722C718.271 32.9171 722.11 32.2396 726.176 32.2396C734.005 32.2396 740.329 34.2722 745.147 38.3375C750.04 42.4028 753.127 47.748 754.407 54.3729L740.404 57.9865C739.952 54.674 738.522 51.8133 736.113 49.4042C733.704 46.9951 730.316 45.7906 725.95 45.7906C723.917 45.7906 721.997 46.167 720.191 46.9198C718.384 47.6727 716.803 48.7643 715.448 50.1947C714.093 51.5498 713.001 53.2436 712.173 55.2763C711.42 57.2337 711.044 59.4545 711.044 61.9389V62.6164C711.044 65.1008 711.42 67.3216 712.173 69.279C713.001 71.1611 714.093 72.742 715.448 74.0219C716.803 75.3017 718.384 76.2803 720.191 76.9579C721.997 77.5602 723.917 77.8613 725.95 77.8613C730.316 77.8613 733.629 76.7697 735.887 74.5865C738.221 72.4033 739.727 69.4672 740.404 65.7783L754.407 69.279Z" fill="#02004E"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
16
assets/pasted-20251017-113610-1935da0a.jpg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="755" height="113" viewBox="0 0 755 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M79.2858 44.5879C79.2858 41.8567 81.4998 39.6427 84.2309 39.6427H106.055C108.786 39.6427 111 41.8567 111 44.5879V58.4832C111 61.2143 108.786 63.4284 106.055 63.4284H84.2309C81.4998 63.4284 79.2858 61.2143 79.2858 58.4832V44.5879Z" fill="#02004E"/>
|
||||||
|
<path d="M0 4.94518C0 2.21404 2.21403 0 4.94518 0H106.055C108.786 0 111 2.21403 111 4.94518V22.8048C111 25.5359 108.786 27.75 106.055 27.75H4.94518C2.21404 27.75 0 25.5359 0 22.8048V4.94518Z" fill="#5C7EF1"/>
|
||||||
|
<path d="M0 80.2665C0 77.5353 2.21403 75.3213 4.94518 75.3213H22.8048C25.5359 75.3213 27.75 77.5353 27.75 80.2665V94.1618C27.75 96.8929 25.5359 99.107 22.8048 99.107H4.94518C2.21404 99.107 0 96.8929 0 94.1618V80.2665Z" fill="#8C9DFF"/>
|
||||||
|
<path d="M43.6071 80.2665C43.6071 77.5353 45.8212 75.3213 48.5523 75.3213H106.055C108.786 75.3213 111 77.5353 111 80.2665V94.1618C111 96.8929 108.786 99.107 106.055 99.107H48.5523C45.8212 99.107 43.6071 96.8929 43.6071 94.1618V80.2665Z" fill="#02004E"/>
|
||||||
|
<path d="M0 44.5879C0 41.8567 2.21403 39.6427 4.94518 39.6427H58.4833C61.2144 39.6427 63.4285 41.8567 63.4285 44.5879V58.4832C63.4285 61.2143 61.2144 63.4284 58.4833 63.4284H4.94518C2.21403 63.4284 0 61.2143 0 58.4832V44.5879Z" fill="#FFA70B"/>
|
||||||
|
<path d="M147.724 10.7838H198.315V25.0124H162.63V43.0804H197.637V57.3089H162.63V89.8314H147.724V10.7838Z" fill="#02004E"/>
|
||||||
|
<path d="M215.401 76.2804H235.05V24.3348H216.078V10.7838H249.278V76.2804H267.572V89.8314H215.401V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M321.133 81.7008C319.627 85.2391 317.519 87.7611 314.809 89.2667C312.099 90.6971 308.937 91.4123 305.323 91.4123C301.936 91.4123 298.736 90.7724 295.725 89.4926C292.789 88.2128 290.191 86.3307 287.933 83.8463C285.674 81.362 283.868 78.3506 282.512 74.8123C281.233 71.274 280.593 67.2463 280.593 62.7293V60.9225C280.593 56.4808 281.233 52.4908 282.512 48.9525C283.792 45.4142 285.524 42.4028 287.707 39.9185C289.89 37.4341 292.412 35.5521 295.273 34.2722C298.209 32.9171 301.333 32.2396 304.646 32.2396C308.636 32.2396 311.835 32.9171 314.244 34.2722C316.729 35.6273 318.686 37.7353 320.117 40.596H322.149V33.8205H336.378V72.8926C336.378 75.1511 337.394 76.2804 339.427 76.2804H341.572V89.8314H331.861C329.301 89.8314 327.193 89.0785 325.537 87.5729C323.956 86.0672 323.166 84.1098 323.166 81.7008H321.133ZM308.485 77.8613C312.626 77.8613 315.938 76.5062 318.423 73.796C320.907 71.0105 322.149 67.2464 322.149 62.5035V61.1484C322.149 56.4055 320.907 52.679 318.423 49.9688C315.938 47.1833 312.626 45.7906 308.485 45.7906C304.345 45.7906 301.032 47.1833 298.548 49.9688C296.064 52.679 294.821 56.4055 294.821 61.1484V62.5035C294.821 67.2464 296.064 71.0105 298.548 73.796C301.032 76.5062 304.345 77.8613 308.485 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M352.448 33.8205H369.047V10.7838H383.276V33.8205H403.49V47.3715H383.276V72.8926C383.276 75.1511 384.292 76.2804 386.325 76.2804H401.457V89.8314H377.743C375.183 89.8314 373.075 89.0032 371.419 87.347C369.838 85.6908 369.047 83.5828 369.047 81.0232V47.3715H352.448V33.8205Z" fill="#02004E"/>
|
||||||
|
<path d="M422.834 76.2804H442.483V24.3348H423.512V10.7838H456.712V76.2804H475.006V89.8314H422.834V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M547.199 62.5035C547.199 67.1711 546.409 71.3117 544.828 74.9253C543.247 78.4636 541.101 81.4749 538.391 83.9593C535.681 86.3683 532.556 88.2128 529.018 89.4926C525.555 90.7724 521.904 91.4123 518.064 91.4123C514.225 91.4123 510.536 90.7724 506.998 89.4926C503.535 88.2128 500.448 86.3683 497.738 83.9593C495.028 81.4749 492.882 78.4636 491.301 74.9253C489.72 71.3117 488.93 67.1711 488.93 62.5035V61.1484C488.93 56.5561 489.72 52.4908 491.301 48.9525C492.882 45.3389 495.028 42.2899 497.738 39.8056C500.448 37.3212 503.535 35.4391 506.998 34.1593C510.536 32.8795 514.225 32.2396 518.064 32.2396C521.904 32.2396 525.555 32.8795 529.018 34.1593C532.556 35.4391 535.681 37.3212 538.391 39.8056C541.101 42.2899 543.247 45.3389 544.828 48.9525C546.409 52.4908 547.199 56.5561 547.199 61.1484V62.5035ZM518.064 77.8613C520.097 77.8613 522.017 77.5225 523.824 76.845C525.63 76.1674 527.211 75.1887 528.566 73.9089C529.922 72.6291 530.976 71.0858 531.728 69.279C532.556 67.3969 532.971 65.289 532.971 62.9552V60.6967C532.971 58.3629 532.556 56.2926 531.728 54.4858C530.976 52.6037 529.922 51.0228 528.566 49.743C527.211 48.4632 525.63 47.4845 523.824 46.8069C522.017 46.1294 520.097 45.7906 518.064 45.7906C516.032 45.7906 514.112 46.1294 512.305 46.8069C510.498 47.4845 508.918 48.4632 507.562 49.743C506.207 51.0228 505.116 52.6037 504.288 54.4858C503.535 56.2926 503.158 58.3629 503.158 60.6967V62.9552C503.158 65.289 503.535 67.3969 504.288 69.279C505.116 71.0858 506.207 72.6291 507.562 73.9089C508.918 75.1887 510.498 76.1674 512.305 76.845C514.112 77.5225 516.032 77.8613 518.064 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M601.438 81.7008H599.405C598.803 82.9806 598.05 84.2227 597.146 85.4273C596.318 86.5565 595.189 87.5729 593.759 88.4763C592.404 89.3797 590.747 90.0948 588.79 90.6218C586.908 91.1488 584.612 91.4123 581.901 91.4123C578.438 91.4123 575.201 90.81 572.19 89.6055C569.254 88.3257 566.657 86.4812 564.398 84.0722C562.215 81.5878 560.483 78.5765 559.204 75.0382C557.999 71.4999 557.397 67.4346 557.397 62.8423V60.8096C557.397 56.2926 558.037 52.265 559.316 48.7266C560.672 45.1883 562.478 42.2146 564.737 39.8056C567.071 37.3212 569.743 35.4391 572.755 34.1593C575.841 32.8795 579.078 32.2396 582.466 32.2396C586.983 32.2396 590.559 33.1053 593.194 34.8369C595.829 36.4931 597.899 38.8645 599.405 41.9511H601.438V33.8205H615.666V103.608C615.666 106.168 614.838 108.276 613.182 109.932C611.601 111.588 609.53 112.416 606.971 112.416H569.254V98.8654H598.389C600.421 98.8654 601.438 97.7361 601.438 95.4776V81.7008ZM586.531 77.8613C590.973 77.8613 594.549 76.4686 597.259 73.6831C600.045 70.8223 601.438 67.0958 601.438 62.5035V61.1484C601.438 56.5561 600.045 52.8672 597.259 50.0817C594.549 47.221 590.973 45.7906 586.531 45.7906C582.09 45.7906 578.476 47.1833 575.691 49.9688C572.98 52.679 571.625 56.4055 571.625 61.1484V62.5035C571.625 67.2464 572.98 71.0105 575.691 73.796C578.476 76.5062 582.09 77.8613 586.531 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M631.623 76.2804H650.594V47.3715H632.978V33.8205H664.823V76.2804H681.084V89.8314H631.623V76.2804ZM668.324 17.2206C668.324 18.7262 668.022 20.1566 667.42 21.5117C666.893 22.7915 666.14 23.9208 665.162 24.8995C664.183 25.8029 663.016 26.5557 661.661 27.158C660.381 27.6849 659.026 27.9484 657.596 27.9484C656.09 27.9484 654.697 27.6849 653.417 27.158C652.138 26.5557 651.008 25.8029 650.03 24.8995C649.051 23.9208 648.261 22.7915 647.658 21.5117C647.131 20.1566 646.868 18.7262 646.868 17.2206C646.868 15.7149 647.131 14.3221 647.658 13.0423C648.261 11.6872 649.051 10.558 650.03 9.65458C651.008 8.67589 652.138 7.92306 653.417 7.39608C654.697 6.79381 656.09 6.49268 657.596 6.49268C659.026 6.49268 660.381 6.79381 661.661 7.39608C663.016 7.92306 664.183 8.67589 665.162 9.65458C666.14 10.558 666.893 11.6872 667.42 13.0423C668.022 14.3221 668.324 15.7149 668.324 17.2206Z" fill="#02004E"/>
|
||||||
|
<path d="M754.407 69.279C753.127 75.9039 750.078 81.2491 745.26 85.3144C740.517 89.3797 734.156 91.4123 726.176 91.4123C722.035 91.4123 718.158 90.7724 714.544 89.4926C711.006 88.2128 707.919 86.3683 705.284 83.9593C702.65 81.5502 700.579 78.5765 699.074 75.0382C697.568 71.4999 696.815 67.4722 696.815 62.9552V61.6001C696.815 57.0831 697.568 53.0178 699.074 49.4042C700.579 45.7906 702.65 42.704 705.284 40.1443C707.995 37.5847 711.119 35.6273 714.657 34.2722C718.271 32.9171 722.11 32.2396 726.176 32.2396C734.005 32.2396 740.329 34.2722 745.147 38.3375C750.04 42.4028 753.127 47.748 754.407 54.3729L740.404 57.9865C739.952 54.674 738.522 51.8133 736.113 49.4042C733.704 46.9951 730.316 45.7906 725.95 45.7906C723.917 45.7906 721.997 46.167 720.191 46.9198C718.384 47.6727 716.803 48.7643 715.448 50.1947C714.093 51.5498 713.001 53.2436 712.173 55.2763C711.42 57.2337 711.044 59.4545 711.044 61.9389V62.6164C711.044 65.1008 711.42 67.3216 712.173 69.279C713.001 71.1611 714.093 72.742 715.448 74.0219C716.803 75.3017 718.384 76.2803 720.191 76.9579C721.997 77.5602 723.917 77.8613 725.95 77.8613C730.316 77.8613 733.629 76.7697 735.887 74.5865C738.221 72.4033 739.727 69.4672 740.404 65.7783L754.407 69.279Z" fill="#02004E"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
16
assets/pasted-20251017-113710-b292ccf6.jpg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="755" height="113" viewBox="0 0 755 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M79.2858 44.5879C79.2858 41.8567 81.4998 39.6427 84.2309 39.6427H106.055C108.786 39.6427 111 41.8567 111 44.5879V58.4832C111 61.2143 108.786 63.4284 106.055 63.4284H84.2309C81.4998 63.4284 79.2858 61.2143 79.2858 58.4832V44.5879Z" fill="#02004E"/>
|
||||||
|
<path d="M0 4.94518C0 2.21404 2.21403 0 4.94518 0H106.055C108.786 0 111 2.21403 111 4.94518V22.8048C111 25.5359 108.786 27.75 106.055 27.75H4.94518C2.21404 27.75 0 25.5359 0 22.8048V4.94518Z" fill="#5C7EF1"/>
|
||||||
|
<path d="M0 80.2665C0 77.5353 2.21403 75.3213 4.94518 75.3213H22.8048C25.5359 75.3213 27.75 77.5353 27.75 80.2665V94.1618C27.75 96.8929 25.5359 99.107 22.8048 99.107H4.94518C2.21404 99.107 0 96.8929 0 94.1618V80.2665Z" fill="#8C9DFF"/>
|
||||||
|
<path d="M43.6071 80.2665C43.6071 77.5353 45.8212 75.3213 48.5523 75.3213H106.055C108.786 75.3213 111 77.5353 111 80.2665V94.1618C111 96.8929 108.786 99.107 106.055 99.107H48.5523C45.8212 99.107 43.6071 96.8929 43.6071 94.1618V80.2665Z" fill="#02004E"/>
|
||||||
|
<path d="M0 44.5879C0 41.8567 2.21403 39.6427 4.94518 39.6427H58.4833C61.2144 39.6427 63.4285 41.8567 63.4285 44.5879V58.4832C63.4285 61.2143 61.2144 63.4284 58.4833 63.4284H4.94518C2.21403 63.4284 0 61.2143 0 58.4832V44.5879Z" fill="#FFA70B"/>
|
||||||
|
<path d="M147.724 10.7838H198.315V25.0124H162.63V43.0804H197.637V57.3089H162.63V89.8314H147.724V10.7838Z" fill="#02004E"/>
|
||||||
|
<path d="M215.401 76.2804H235.05V24.3348H216.078V10.7838H249.278V76.2804H267.572V89.8314H215.401V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M321.133 81.7008C319.627 85.2391 317.519 87.7611 314.809 89.2667C312.099 90.6971 308.937 91.4123 305.323 91.4123C301.936 91.4123 298.736 90.7724 295.725 89.4926C292.789 88.2128 290.191 86.3307 287.933 83.8463C285.674 81.362 283.868 78.3506 282.512 74.8123C281.233 71.274 280.593 67.2463 280.593 62.7293V60.9225C280.593 56.4808 281.233 52.4908 282.512 48.9525C283.792 45.4142 285.524 42.4028 287.707 39.9185C289.89 37.4341 292.412 35.5521 295.273 34.2722C298.209 32.9171 301.333 32.2396 304.646 32.2396C308.636 32.2396 311.835 32.9171 314.244 34.2722C316.729 35.6273 318.686 37.7353 320.117 40.596H322.149V33.8205H336.378V72.8926C336.378 75.1511 337.394 76.2804 339.427 76.2804H341.572V89.8314H331.861C329.301 89.8314 327.193 89.0785 325.537 87.5729C323.956 86.0672 323.166 84.1098 323.166 81.7008H321.133ZM308.485 77.8613C312.626 77.8613 315.938 76.5062 318.423 73.796C320.907 71.0105 322.149 67.2464 322.149 62.5035V61.1484C322.149 56.4055 320.907 52.679 318.423 49.9688C315.938 47.1833 312.626 45.7906 308.485 45.7906C304.345 45.7906 301.032 47.1833 298.548 49.9688C296.064 52.679 294.821 56.4055 294.821 61.1484V62.5035C294.821 67.2464 296.064 71.0105 298.548 73.796C301.032 76.5062 304.345 77.8613 308.485 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M352.448 33.8205H369.047V10.7838H383.276V33.8205H403.49V47.3715H383.276V72.8926C383.276 75.1511 384.292 76.2804 386.325 76.2804H401.457V89.8314H377.743C375.183 89.8314 373.075 89.0032 371.419 87.347C369.838 85.6908 369.047 83.5828 369.047 81.0232V47.3715H352.448V33.8205Z" fill="#02004E"/>
|
||||||
|
<path d="M422.834 76.2804H442.483V24.3348H423.512V10.7838H456.712V76.2804H475.006V89.8314H422.834V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M547.199 62.5035C547.199 67.1711 546.409 71.3117 544.828 74.9253C543.247 78.4636 541.101 81.4749 538.391 83.9593C535.681 86.3683 532.556 88.2128 529.018 89.4926C525.555 90.7724 521.904 91.4123 518.064 91.4123C514.225 91.4123 510.536 90.7724 506.998 89.4926C503.535 88.2128 500.448 86.3683 497.738 83.9593C495.028 81.4749 492.882 78.4636 491.301 74.9253C489.72 71.3117 488.93 67.1711 488.93 62.5035V61.1484C488.93 56.5561 489.72 52.4908 491.301 48.9525C492.882 45.3389 495.028 42.2899 497.738 39.8056C500.448 37.3212 503.535 35.4391 506.998 34.1593C510.536 32.8795 514.225 32.2396 518.064 32.2396C521.904 32.2396 525.555 32.8795 529.018 34.1593C532.556 35.4391 535.681 37.3212 538.391 39.8056C541.101 42.2899 543.247 45.3389 544.828 48.9525C546.409 52.4908 547.199 56.5561 547.199 61.1484V62.5035ZM518.064 77.8613C520.097 77.8613 522.017 77.5225 523.824 76.845C525.63 76.1674 527.211 75.1887 528.566 73.9089C529.922 72.6291 530.976 71.0858 531.728 69.279C532.556 67.3969 532.971 65.289 532.971 62.9552V60.6967C532.971 58.3629 532.556 56.2926 531.728 54.4858C530.976 52.6037 529.922 51.0228 528.566 49.743C527.211 48.4632 525.63 47.4845 523.824 46.8069C522.017 46.1294 520.097 45.7906 518.064 45.7906C516.032 45.7906 514.112 46.1294 512.305 46.8069C510.498 47.4845 508.918 48.4632 507.562 49.743C506.207 51.0228 505.116 52.6037 504.288 54.4858C503.535 56.2926 503.158 58.3629 503.158 60.6967V62.9552C503.158 65.289 503.535 67.3969 504.288 69.279C505.116 71.0858 506.207 72.6291 507.562 73.9089C508.918 75.1887 510.498 76.1674 512.305 76.845C514.112 77.5225 516.032 77.8613 518.064 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M601.438 81.7008H599.405C598.803 82.9806 598.05 84.2227 597.146 85.4273C596.318 86.5565 595.189 87.5729 593.759 88.4763C592.404 89.3797 590.747 90.0948 588.79 90.6218C586.908 91.1488 584.612 91.4123 581.901 91.4123C578.438 91.4123 575.201 90.81 572.19 89.6055C569.254 88.3257 566.657 86.4812 564.398 84.0722C562.215 81.5878 560.483 78.5765 559.204 75.0382C557.999 71.4999 557.397 67.4346 557.397 62.8423V60.8096C557.397 56.2926 558.037 52.265 559.316 48.7266C560.672 45.1883 562.478 42.2146 564.737 39.8056C567.071 37.3212 569.743 35.4391 572.755 34.1593C575.841 32.8795 579.078 32.2396 582.466 32.2396C586.983 32.2396 590.559 33.1053 593.194 34.8369C595.829 36.4931 597.899 38.8645 599.405 41.9511H601.438V33.8205H615.666V103.608C615.666 106.168 614.838 108.276 613.182 109.932C611.601 111.588 609.53 112.416 606.971 112.416H569.254V98.8654H598.389C600.421 98.8654 601.438 97.7361 601.438 95.4776V81.7008ZM586.531 77.8613C590.973 77.8613 594.549 76.4686 597.259 73.6831C600.045 70.8223 601.438 67.0958 601.438 62.5035V61.1484C601.438 56.5561 600.045 52.8672 597.259 50.0817C594.549 47.221 590.973 45.7906 586.531 45.7906C582.09 45.7906 578.476 47.1833 575.691 49.9688C572.98 52.679 571.625 56.4055 571.625 61.1484V62.5035C571.625 67.2464 572.98 71.0105 575.691 73.796C578.476 76.5062 582.09 77.8613 586.531 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M631.623 76.2804H650.594V47.3715H632.978V33.8205H664.823V76.2804H681.084V89.8314H631.623V76.2804ZM668.324 17.2206C668.324 18.7262 668.022 20.1566 667.42 21.5117C666.893 22.7915 666.14 23.9208 665.162 24.8995C664.183 25.8029 663.016 26.5557 661.661 27.158C660.381 27.6849 659.026 27.9484 657.596 27.9484C656.09 27.9484 654.697 27.6849 653.417 27.158C652.138 26.5557 651.008 25.8029 650.03 24.8995C649.051 23.9208 648.261 22.7915 647.658 21.5117C647.131 20.1566 646.868 18.7262 646.868 17.2206C646.868 15.7149 647.131 14.3221 647.658 13.0423C648.261 11.6872 649.051 10.558 650.03 9.65458C651.008 8.67589 652.138 7.92306 653.417 7.39608C654.697 6.79381 656.09 6.49268 657.596 6.49268C659.026 6.49268 660.381 6.79381 661.661 7.39608C663.016 7.92306 664.183 8.67589 665.162 9.65458C666.14 10.558 666.893 11.6872 667.42 13.0423C668.022 14.3221 668.324 15.7149 668.324 17.2206Z" fill="#02004E"/>
|
||||||
|
<path d="M754.407 69.279C753.127 75.9039 750.078 81.2491 745.26 85.3144C740.517 89.3797 734.156 91.4123 726.176 91.4123C722.035 91.4123 718.158 90.7724 714.544 89.4926C711.006 88.2128 707.919 86.3683 705.284 83.9593C702.65 81.5502 700.579 78.5765 699.074 75.0382C697.568 71.4999 696.815 67.4722 696.815 62.9552V61.6001C696.815 57.0831 697.568 53.0178 699.074 49.4042C700.579 45.7906 702.65 42.704 705.284 40.1443C707.995 37.5847 711.119 35.6273 714.657 34.2722C718.271 32.9171 722.11 32.2396 726.176 32.2396C734.005 32.2396 740.329 34.2722 745.147 38.3375C750.04 42.4028 753.127 47.748 754.407 54.3729L740.404 57.9865C739.952 54.674 738.522 51.8133 736.113 49.4042C733.704 46.9951 730.316 45.7906 725.95 45.7906C723.917 45.7906 721.997 46.167 720.191 46.9198C718.384 47.6727 716.803 48.7643 715.448 50.1947C714.093 51.5498 713.001 53.2436 712.173 55.2763C711.42 57.2337 711.044 59.4545 711.044 61.9389V62.6164C711.044 65.1008 711.42 67.3216 712.173 69.279C713.001 71.1611 714.093 72.742 715.448 74.0219C716.803 75.3017 718.384 76.2803 720.191 76.9579C721.997 77.5602 723.917 77.8613 725.95 77.8613C730.316 77.8613 733.629 76.7697 735.887 74.5865C738.221 72.4033 739.727 69.4672 740.404 65.7783L754.407 69.279Z" fill="#02004E"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
16
assets/pasted-20251017-113909-97a4d5fe.jpg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="755" height="113" viewBox="0 0 755 113" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M79.2858 44.5879C79.2858 41.8567 81.4998 39.6427 84.2309 39.6427H106.055C108.786 39.6427 111 41.8567 111 44.5879V58.4832C111 61.2143 108.786 63.4284 106.055 63.4284H84.2309C81.4998 63.4284 79.2858 61.2143 79.2858 58.4832V44.5879Z" fill="#02004E"/>
|
||||||
|
<path d="M0 4.94518C0 2.21404 2.21403 0 4.94518 0H106.055C108.786 0 111 2.21403 111 4.94518V22.8048C111 25.5359 108.786 27.75 106.055 27.75H4.94518C2.21404 27.75 0 25.5359 0 22.8048V4.94518Z" fill="#5C7EF1"/>
|
||||||
|
<path d="M0 80.2665C0 77.5353 2.21403 75.3213 4.94518 75.3213H22.8048C25.5359 75.3213 27.75 77.5353 27.75 80.2665V94.1618C27.75 96.8929 25.5359 99.107 22.8048 99.107H4.94518C2.21404 99.107 0 96.8929 0 94.1618V80.2665Z" fill="#8C9DFF"/>
|
||||||
|
<path d="M43.6071 80.2665C43.6071 77.5353 45.8212 75.3213 48.5523 75.3213H106.055C108.786 75.3213 111 77.5353 111 80.2665V94.1618C111 96.8929 108.786 99.107 106.055 99.107H48.5523C45.8212 99.107 43.6071 96.8929 43.6071 94.1618V80.2665Z" fill="#02004E"/>
|
||||||
|
<path d="M0 44.5879C0 41.8567 2.21403 39.6427 4.94518 39.6427H58.4833C61.2144 39.6427 63.4285 41.8567 63.4285 44.5879V58.4832C63.4285 61.2143 61.2144 63.4284 58.4833 63.4284H4.94518C2.21403 63.4284 0 61.2143 0 58.4832V44.5879Z" fill="#FFA70B"/>
|
||||||
|
<path d="M147.724 10.7838H198.315V25.0124H162.63V43.0804H197.637V57.3089H162.63V89.8314H147.724V10.7838Z" fill="#02004E"/>
|
||||||
|
<path d="M215.401 76.2804H235.05V24.3348H216.078V10.7838H249.278V76.2804H267.572V89.8314H215.401V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M321.133 81.7008C319.627 85.2391 317.519 87.7611 314.809 89.2667C312.099 90.6971 308.937 91.4123 305.323 91.4123C301.936 91.4123 298.736 90.7724 295.725 89.4926C292.789 88.2128 290.191 86.3307 287.933 83.8463C285.674 81.362 283.868 78.3506 282.512 74.8123C281.233 71.274 280.593 67.2463 280.593 62.7293V60.9225C280.593 56.4808 281.233 52.4908 282.512 48.9525C283.792 45.4142 285.524 42.4028 287.707 39.9185C289.89 37.4341 292.412 35.5521 295.273 34.2722C298.209 32.9171 301.333 32.2396 304.646 32.2396C308.636 32.2396 311.835 32.9171 314.244 34.2722C316.729 35.6273 318.686 37.7353 320.117 40.596H322.149V33.8205H336.378V72.8926C336.378 75.1511 337.394 76.2804 339.427 76.2804H341.572V89.8314H331.861C329.301 89.8314 327.193 89.0785 325.537 87.5729C323.956 86.0672 323.166 84.1098 323.166 81.7008H321.133ZM308.485 77.8613C312.626 77.8613 315.938 76.5062 318.423 73.796C320.907 71.0105 322.149 67.2464 322.149 62.5035V61.1484C322.149 56.4055 320.907 52.679 318.423 49.9688C315.938 47.1833 312.626 45.7906 308.485 45.7906C304.345 45.7906 301.032 47.1833 298.548 49.9688C296.064 52.679 294.821 56.4055 294.821 61.1484V62.5035C294.821 67.2464 296.064 71.0105 298.548 73.796C301.032 76.5062 304.345 77.8613 308.485 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M352.448 33.8205H369.047V10.7838H383.276V33.8205H403.49V47.3715H383.276V72.8926C383.276 75.1511 384.292 76.2804 386.325 76.2804H401.457V89.8314H377.743C375.183 89.8314 373.075 89.0032 371.419 87.347C369.838 85.6908 369.047 83.5828 369.047 81.0232V47.3715H352.448V33.8205Z" fill="#02004E"/>
|
||||||
|
<path d="M422.834 76.2804H442.483V24.3348H423.512V10.7838H456.712V76.2804H475.006V89.8314H422.834V76.2804Z" fill="#02004E"/>
|
||||||
|
<path d="M547.199 62.5035C547.199 67.1711 546.409 71.3117 544.828 74.9253C543.247 78.4636 541.101 81.4749 538.391 83.9593C535.681 86.3683 532.556 88.2128 529.018 89.4926C525.555 90.7724 521.904 91.4123 518.064 91.4123C514.225 91.4123 510.536 90.7724 506.998 89.4926C503.535 88.2128 500.448 86.3683 497.738 83.9593C495.028 81.4749 492.882 78.4636 491.301 74.9253C489.72 71.3117 488.93 67.1711 488.93 62.5035V61.1484C488.93 56.5561 489.72 52.4908 491.301 48.9525C492.882 45.3389 495.028 42.2899 497.738 39.8056C500.448 37.3212 503.535 35.4391 506.998 34.1593C510.536 32.8795 514.225 32.2396 518.064 32.2396C521.904 32.2396 525.555 32.8795 529.018 34.1593C532.556 35.4391 535.681 37.3212 538.391 39.8056C541.101 42.2899 543.247 45.3389 544.828 48.9525C546.409 52.4908 547.199 56.5561 547.199 61.1484V62.5035ZM518.064 77.8613C520.097 77.8613 522.017 77.5225 523.824 76.845C525.63 76.1674 527.211 75.1887 528.566 73.9089C529.922 72.6291 530.976 71.0858 531.728 69.279C532.556 67.3969 532.971 65.289 532.971 62.9552V60.6967C532.971 58.3629 532.556 56.2926 531.728 54.4858C530.976 52.6037 529.922 51.0228 528.566 49.743C527.211 48.4632 525.63 47.4845 523.824 46.8069C522.017 46.1294 520.097 45.7906 518.064 45.7906C516.032 45.7906 514.112 46.1294 512.305 46.8069C510.498 47.4845 508.918 48.4632 507.562 49.743C506.207 51.0228 505.116 52.6037 504.288 54.4858C503.535 56.2926 503.158 58.3629 503.158 60.6967V62.9552C503.158 65.289 503.535 67.3969 504.288 69.279C505.116 71.0858 506.207 72.6291 507.562 73.9089C508.918 75.1887 510.498 76.1674 512.305 76.845C514.112 77.5225 516.032 77.8613 518.064 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M601.438 81.7008H599.405C598.803 82.9806 598.05 84.2227 597.146 85.4273C596.318 86.5565 595.189 87.5729 593.759 88.4763C592.404 89.3797 590.747 90.0948 588.79 90.6218C586.908 91.1488 584.612 91.4123 581.901 91.4123C578.438 91.4123 575.201 90.81 572.19 89.6055C569.254 88.3257 566.657 86.4812 564.398 84.0722C562.215 81.5878 560.483 78.5765 559.204 75.0382C557.999 71.4999 557.397 67.4346 557.397 62.8423V60.8096C557.397 56.2926 558.037 52.265 559.316 48.7266C560.672 45.1883 562.478 42.2146 564.737 39.8056C567.071 37.3212 569.743 35.4391 572.755 34.1593C575.841 32.8795 579.078 32.2396 582.466 32.2396C586.983 32.2396 590.559 33.1053 593.194 34.8369C595.829 36.4931 597.899 38.8645 599.405 41.9511H601.438V33.8205H615.666V103.608C615.666 106.168 614.838 108.276 613.182 109.932C611.601 111.588 609.53 112.416 606.971 112.416H569.254V98.8654H598.389C600.421 98.8654 601.438 97.7361 601.438 95.4776V81.7008ZM586.531 77.8613C590.973 77.8613 594.549 76.4686 597.259 73.6831C600.045 70.8223 601.438 67.0958 601.438 62.5035V61.1484C601.438 56.5561 600.045 52.8672 597.259 50.0817C594.549 47.221 590.973 45.7906 586.531 45.7906C582.09 45.7906 578.476 47.1833 575.691 49.9688C572.98 52.679 571.625 56.4055 571.625 61.1484V62.5035C571.625 67.2464 572.98 71.0105 575.691 73.796C578.476 76.5062 582.09 77.8613 586.531 77.8613Z" fill="#02004E"/>
|
||||||
|
<path d="M631.623 76.2804H650.594V47.3715H632.978V33.8205H664.823V76.2804H681.084V89.8314H631.623V76.2804ZM668.324 17.2206C668.324 18.7262 668.022 20.1566 667.42 21.5117C666.893 22.7915 666.14 23.9208 665.162 24.8995C664.183 25.8029 663.016 26.5557 661.661 27.158C660.381 27.6849 659.026 27.9484 657.596 27.9484C656.09 27.9484 654.697 27.6849 653.417 27.158C652.138 26.5557 651.008 25.8029 650.03 24.8995C649.051 23.9208 648.261 22.7915 647.658 21.5117C647.131 20.1566 646.868 18.7262 646.868 17.2206C646.868 15.7149 647.131 14.3221 647.658 13.0423C648.261 11.6872 649.051 10.558 650.03 9.65458C651.008 8.67589 652.138 7.92306 653.417 7.39608C654.697 6.79381 656.09 6.49268 657.596 6.49268C659.026 6.49268 660.381 6.79381 661.661 7.39608C663.016 7.92306 664.183 8.67589 665.162 9.65458C666.14 10.558 666.893 11.6872 667.42 13.0423C668.022 14.3221 668.324 15.7149 668.324 17.2206Z" fill="#02004E"/>
|
||||||
|
<path d="M754.407 69.279C753.127 75.9039 750.078 81.2491 745.26 85.3144C740.517 89.3797 734.156 91.4123 726.176 91.4123C722.035 91.4123 718.158 90.7724 714.544 89.4926C711.006 88.2128 707.919 86.3683 705.284 83.9593C702.65 81.5502 700.579 78.5765 699.074 75.0382C697.568 71.4999 696.815 67.4722 696.815 62.9552V61.6001C696.815 57.0831 697.568 53.0178 699.074 49.4042C700.579 45.7906 702.65 42.704 705.284 40.1443C707.995 37.5847 711.119 35.6273 714.657 34.2722C718.271 32.9171 722.11 32.2396 726.176 32.2396C734.005 32.2396 740.329 34.2722 745.147 38.3375C750.04 42.4028 753.127 47.748 754.407 54.3729L740.404 57.9865C739.952 54.674 738.522 51.8133 736.113 49.4042C733.704 46.9951 730.316 45.7906 725.95 45.7906C723.917 45.7906 721.997 46.167 720.191 46.9198C718.384 47.6727 716.803 48.7643 715.448 50.1947C714.093 51.5498 713.001 53.2436 712.173 55.2763C711.42 57.2337 711.044 59.4545 711.044 61.9389V62.6164C711.044 65.1008 711.42 67.3216 712.173 69.279C713.001 71.1611 714.093 72.742 715.448 74.0219C716.803 75.3017 718.384 76.2803 720.191 76.9579C721.997 77.5602 723.917 77.8613 725.95 77.8613C730.316 77.8613 733.629 76.7697 735.887 74.5865C738.221 72.4033 739.727 69.4672 740.404 65.7783L754.407 69.279Z" fill="#02004E"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
BIN
assets/pasted-20251017-114417-852523c9.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
assets/pasted-20251017-114555-f353c4d9.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/pasted-20251017-115404-4e52d477.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/pasted-20251017-115640-64b19aff.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
assets/pasted-20251017-115952-0d605af0.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/pasted-20251017-120438-de869d16.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/pasted-20251025-190102-dd19def2.png
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
assets/pasted-20251025-190534-b34b4e03.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
assets/pasted-20251025-190634-62543fd4.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
assets/pasted-20251025-190711-b09e530a.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
assets/pasted-20251025-190857-c1b4b325.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/pasted-20251025-191303-2c7c1987.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/pasted-20251030-095539-d98e7dee.jpg
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
assets/pasted-20251030-095744-1b7c02ab.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
assets/vm-shot-2025-10-16T14-15-36-087Z.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
assets/vm-shot-2025-10-16T16-02-18-334Z.jpg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/vm-shot-2025-10-17T09-58-48-783Z.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/vm-shot-2025-10-17T10-52-25-776Z.jpg
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
assets/vm-shot-2025-10-17T11-08-35-321Z.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
assets/vm-shot-2025-10-25T19-08-54-931Z.jpg
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
assets/vm-shot-2025-10-30T09-55-34-105Z.jpg
Normal file
|
After Width: | Height: | Size: 96 KiB |
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>
|
||||||
49
db/migrate.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Connect without specifying a database to ensure the DB exists
|
||||||
|
$pdo = new PDO('mysql:host='.DB_HOST, DB_USER, DB_PASS, [
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
]);
|
||||||
|
$pdo->exec('CREATE DATABASE IF NOT EXISTS '.DB_NAME);
|
||||||
|
|
||||||
|
// Now connect to the specific database
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// 1. Create migrations table if it doesn't exist
|
||||||
|
$pdo->exec('CREATE TABLE IF NOT EXISTS migrations (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
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) {
|
||||||
|
die("Database migration failed: " . $e->getMessage());
|
||||||
|
}
|
||||||
9
db/migrations/001_create_webinars_table.sql
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS webinars (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
presenter VARCHAR(255),
|
||||||
|
scheduled_at DATETIME NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
10
db/migrations/002_create_attendees_table.sql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS attendees (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
webinar_id INT NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
email VARCHAR(255) NOT NULL,
|
||||||
|
company VARCHAR(255),
|
||||||
|
registered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (webinar_id) REFERENCES webinars(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
5
db/migrations/003_seed_webinars_table.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
INSERT INTO webinars (title, description, presenter, scheduled_at) VALUES
|
||||||
|
('Getting Started with Docker', 'Learn the fundamentals of Docker and containerization.', 'John Doe', '2025-11-15 10:00:00'),
|
||||||
|
('Mastering Kubernetes', 'A deep dive into Kubernetes for container orchestration.', 'Jane Smith', '2025-11-20 14:00:00'),
|
||||||
|
('CI/CD with Jenkins', 'Automate your build, test, and deployment pipeline with Jenkins.', 'Peter Jones', '2025-12-01 11:30:00');
|
||||||
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
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `attendees` ADD `password` VARCHAR(255) NOT NULL;
|
||||||
1
db/migrations/006_add_form_fields.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `attendees` ADD `consented` TINYINT(1) NOT NULL DEFAULT 0;
|
||||||
1
db/migrations/007_add_consented_column.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE `attendees` ADD `consented` TINYINT(1) NOT NULL DEFAULT 0;
|
||||||
1
db/migrations/008_rename_column.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE attendees CHANGE COLUMN registered_at created_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;
|
||||||
1
db/migrations/009_add_deleted_at_to_attendees.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE attendees ADD COLUMN deleted_at TIMESTAMP NULL DEFAULT NULL;
|
||||||
1
db/migrations/010_add_how_did_you_hear_to_attendees.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE attendees ADD COLUMN how_did_you_hear VARCHAR(255) DEFAULT NULL;
|
||||||
29
delete_attendee.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user']) || $_SESSION['user'] !== 'admin') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['id'])) {
|
||||||
|
$id = $_POST['id'];
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM attendees WHERE id = ?");
|
||||||
|
|
||||||
|
if ($stmt->execute([$id])) {
|
||||||
|
if ($stmt->rowCount() > 0) {
|
||||||
|
$_SESSION['message'] = "Attendee with ID $id has been deleted successfully.";
|
||||||
|
} else {
|
||||||
|
$_SESSION['message'] = "Error: No attendee found with ID $id. Nothing was deleted.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$_SESSION['message'] = "Error: Could not execute the delete statement.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$_SESSION['message'] = "Error: Invalid request.";
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
115
edit_attendee.php
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attendee = null;
|
||||||
|
$message = '';
|
||||||
|
|
||||||
|
if (!isset($_GET['id']) && $_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$id = $_GET['id'] ?? $_POST['id'];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
// Update logic
|
||||||
|
$fields = ['first_name', 'last_name', 'email', 'company', 'how_did_you_hear', 'consented'];
|
||||||
|
$sql = 'UPDATE attendees SET ';
|
||||||
|
$params = [];
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
if (isset($_POST[$field])) {
|
||||||
|
$sql .= "$field = ?, ";
|
||||||
|
$params[] = $_POST[$field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sql = rtrim($sql, ', ') . ' WHERE id = ?';
|
||||||
|
$params[] = $_POST['id'];
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch logic
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM attendees WHERE id = ?');
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$attendee = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$attendee) {
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Database error: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Edit Attendee</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 {
|
||||||
|
max-width: 600px;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="mb-4">Edit Attendee #<?= htmlspecialchars($attendee['id']) ?></h1>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="id" value="<?= htmlspecialchars($attendee['id']) ?>">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="first_name" class="form-label">First Name</label>
|
||||||
|
<input type="text" class="form-control" id="first_name" name="first_name" value="<?= htmlspecialchars($attendee['first_name']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="last_name" class="form-label">Last Name</label>
|
||||||
|
<input type="text" class="form-control" id="last_name" name="last_name" value="<?= htmlspecialchars($attendee['last_name']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="email" class="form-label">Email</label>
|
||||||
|
<input type="email" class="form-control" id="email" name="email" value="<?= htmlspecialchars($attendee['email']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="company" class="form-label">Company</label>
|
||||||
|
<input type="text" class="form-control" id="company" name="company" value="<?= htmlspecialchars($attendee['company']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="how_did_you_hear" class="form-label">How did you hear?</label>
|
||||||
|
<input type="text" class="form-control" id="how_did_you_hear" name="how_did_you_hear" value="<?= htmlspecialchars($attendee['how_did_you_hear']) ?>">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<input type="hidden" name="consented" value="0">
|
||||||
|
<input type="checkbox" class="form-check-input" id="consented" name="consented" value="1" <?= $attendee['consented'] ? 'checked' : '' ?>>
|
||||||
|
<label class="form-check-label" for="consented">Consented</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||||
|
<a href="admin.php" class="btn btn-secondary">Cancel</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
39
export_csv.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// Check if user is admin
|
||||||
|
if (!isset($_SESSION['user']) || $_SESSION['user'] !== 'admin') {
|
||||||
|
http_response_code(403);
|
||||||
|
echo "Forbidden";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT first_name, last_name, email, how_did_you_hear, company FROM attendees ORDER BY created_at DESC");
|
||||||
|
$attendees = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
header('Content-Type: text/csv; charset=utf-8');
|
||||||
|
header('Content-Disposition: attachment; filename=attendees.csv');
|
||||||
|
|
||||||
|
$output = fopen('php://output', 'w');
|
||||||
|
|
||||||
|
// Add BOM to fix UTF-8 in Excel
|
||||||
|
fputs($output, "\xEF\xBB\xBF");
|
||||||
|
|
||||||
|
// Add header row
|
||||||
|
fputcsv($output, ['First Name', 'Last Name', 'Email', 'Source', 'Company']);
|
||||||
|
|
||||||
|
// Add data rows
|
||||||
|
foreach ($attendees as $attendee) {
|
||||||
|
fputcsv($output, [
|
||||||
|
$attendee['first_name'],
|
||||||
|
$attendee['last_name'],
|
||||||
|
$attendee['email'],
|
||||||
|
$attendee['how_did_you_hear'] ?? '',
|
||||||
|
$attendee['company'] ?? ''
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($output);
|
||||||
|
exit;
|
||||||
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;
|
||||||
|
}
|
||||||
|
?>
|
||||||
394
index.php
@ -1,150 +1,284 @@
|
|||||||
<?php
|
<!DOCTYPE html>
|
||||||
declare(strict_types=1);
|
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
|
||||||
<!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>New Style</title>
|
<title>Webinar Registration</title>
|
||||||
<?php
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
// Read project preview data from environment
|
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($projectImageUrl): ?>
|
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<!-- Twitter image -->
|
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||||
font-family: 'Inter', sans-serif;
|
background: linear-gradient(135deg, #1a237e 0%, #673ab7 100%);
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
color: #fff;
|
||||||
color: var(--text-color);
|
}
|
||||||
display: flex;
|
.webinar-container {
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
text-align: center;
|
display: flex;
|
||||||
overflow: hidden;
|
align-items: center;
|
||||||
position: relative;
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
body::before {
|
.webinar-card {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
max-width: 1200px;
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
box-shadow: 0 20px 50px rgba(0,0,0,0.25);
|
||||||
animation: bg-pan 20s linear infinite;
|
border-radius: 20px;
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% { background-position: 0% 0%; }
|
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
clip: rect(0, 0, 0, 0);
|
background: rgba(255, 255, 255, 0.05);
|
||||||
white-space: nowrap; border: 0;
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
h1 {
|
.left-column {
|
||||||
font-size: 3rem;
|
padding: 60px;
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
}
|
||||||
p {
|
.right-column {
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
padding: 2px 6px;
|
padding: 60px;
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
}
|
||||||
footer {
|
.brand-logo {
|
||||||
position: absolute;
|
font-weight: bold;
|
||||||
bottom: 1rem;
|
font-size: 1.5rem;
|
||||||
font-size: 0.8rem;
|
margin-bottom: 40px;
|
||||||
opacity: 0.7;
|
}
|
||||||
|
.webinar-title {
|
||||||
|
font-size: 2.8rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.webinar-subtitle {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.webinar-time {
|
||||||
|
color: #bdbdbd;
|
||||||
|
margin-top: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.webinar-description {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.speakers-section {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
.speakers-section h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.speaker-card {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.speaker-card img {
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 4px solid white;
|
||||||
|
}
|
||||||
|
.speaker-card h5 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.speaker-card .text-muted {
|
||||||
|
color: #bdbdbd !important;
|
||||||
|
}
|
||||||
|
.form-control {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 15px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.form-control::placeholder {
|
||||||
|
color: #bdbdbd;
|
||||||
|
}
|
||||||
|
.form-check-label {
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.btn-register {
|
||||||
|
background: linear-gradient(135deg, #3f51b5 0%, #9c27b0 100%);
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.btn-register:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
.what-you-learn {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
.what-you-learn h4 {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.what-you-learn ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.what-you-learn li {
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
.what-you-learn li::before {
|
||||||
|
content: "✓";
|
||||||
|
margin-right: 10px;
|
||||||
|
color: #3f51b5;
|
||||||
|
}
|
||||||
|
#right-column-content h2, #right-column-content p {
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<div class="webinar-container">
|
||||||
<div class="card">
|
<div class="webinar-card">
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<div class="row g-0">
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<div class="col-lg-7">
|
||||||
<span class="sr-only">Loading…</span>
|
<div class="left-column">
|
||||||
|
<img src="assets/pasted-20251030-095744-1b7c02ab.png" alt="AppWizzy Logo" style="height: 60px; margin-bottom: 40px;">
|
||||||
|
|
||||||
|
<h1 class="webinar-title" style="line-height: 1.2;">Building Scalable Apps with AppWizzy</h1>
|
||||||
|
<h2 class="webinar-subtitle">Professional Vibe-Coding Webinar</h2>
|
||||||
|
<div class="webinar-time">WEDNESDAY, NOVEMBER 19 | 10AM EST | 7AM PST | 4PM CET</div>
|
||||||
|
<p class="webinar-description">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="speakers-section">
|
||||||
|
<h3>Speakers</h3>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="speaker-card">
|
||||||
|
<img src="assets/pasted-20251025-190534-b34b4e03.png" alt="Philip Daineka">
|
||||||
|
<h5>Philip Daineka</h5>
|
||||||
|
<p class="text-muted">CEO, AppWizzy</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
<div class="col-md-4">
|
||||||
<footer>
|
<div class="speaker-card">
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<img src="assets/pasted-20251025-190634-62543fd4.png" alt="Alexandr Rubanau">
|
||||||
</footer>
|
<h5>Alexandr Rubanau</h5>
|
||||||
|
<p class="text-muted">Lead Engineer</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="speaker-card">
|
||||||
|
<img src="assets/pasted-20251025-190711-b09e530a.png" alt="Alexey Vertel">
|
||||||
|
<h5>Alexey Vertel</h5>
|
||||||
|
<p class="text-muted">Lead Engineer</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-5">
|
||||||
|
<div class="right-column" id="right-column-content">
|
||||||
|
<h2>Register Form</h2>
|
||||||
|
<p>Don't miss this chance to learn and get your questions answered</p>
|
||||||
|
<div id="form-message" class="mb-3" style="color: #ffc107;"></div>
|
||||||
|
<form id="registration-form" method="POST" action="register.php">
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col">
|
||||||
|
<input type="text" class="form-control" name="first_name" placeholder="First name" required>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<input type="text" class="form-control" name="last_name" placeholder="Last name" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="email" class="form-control" name="email" placeholder="email@website.com" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<input type="text" class="form-control" name="company" placeholder="Company">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<select class="form-select form-control" name="how_did_you_hear">
|
||||||
|
<option selected>How did you hear about this webinar?</option>
|
||||||
|
<option value="Social Media">Social Media (X, Instargram, Facebook, etc.)</option>
|
||||||
|
<option value="LinkedIn">LinkedIn</option>
|
||||||
|
<option value="Reddit">Reddit</option>
|
||||||
|
<option value="Threads">Threads</option>
|
||||||
|
<option value="Advertisement">Advertisement</option>
|
||||||
|
<option value="ChatGPT">ChatGPT</option>
|
||||||
|
<option value="Flatlogic Community Discord">Flatlogic Community Discord</option>
|
||||||
|
<option value="Other">Other</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="webinar_id" value="1">
|
||||||
|
<button type="submit" class="btn-register">Register Now</button>
|
||||||
|
</form>
|
||||||
|
<div class="what-you-learn">
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||||
|
<img src="assets/pasted-20251017-115404-4e52d477.png" alt="Bot" style="width: 50px; margin-right: 15px;">
|
||||||
|
<h4>SCHEDULE</h4>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Intro ~10 min</li>
|
||||||
|
<li>Creating Apps ~40-50 min</li>
|
||||||
|
<li>Q&A</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('registration-form').addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const form = event.target;
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const messageDiv = document.getElementById('form-message');
|
||||||
|
const rightColumn = document.getElementById('right-column-content');
|
||||||
|
|
||||||
|
messageDiv.textContent = ''; // Clear previous messages
|
||||||
|
|
||||||
|
fetch('register.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
const successHtml = `
|
||||||
|
<h2>You're Registered!</h2>
|
||||||
|
<p>Thank you for registering for the webinar. An email confirmation has been sent to you.</p>
|
||||||
|
<p>Add this event to your calendar:</p>
|
||||||
|
<div class="d-grid gap-2 mt-4">
|
||||||
|
<a href="${data.google_link}" target="_blank" class="btn btn-light">Add to Google Calendar</a>
|
||||||
|
<a href="${data.outlook_link}" download="webinar.ics" class="btn btn-outline-light">Add to Outlook/iCal</a>
|
||||||
|
</div>
|
||||||
|
<div class="what-you-learn" style="margin-top: 60px;">
|
||||||
|
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||||
|
<img src="assets/pasted-20251017-115404-4e52d477.png" alt="Bot" style="width: 50px; margin-right: 15px;">
|
||||||
|
<h4>SCHEDULE</h4>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li>Intro ~10 min</li>
|
||||||
|
<li>Creating Apps ~40-50 min</li>
|
||||||
|
<li>Q&A</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
rightColumn.innerHTML = successHtml;
|
||||||
|
} else {
|
||||||
|
messageDiv.textContent = data.error || 'An unknown error occurred.';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
messageDiv.textContent = 'A network error occurred. Please try again.';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
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>
|
||||||
200
login.php
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
<?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
|
||||||
|
// Hardcoded admin credentials (consider a more secure solution)
|
||||||
|
if ($email === 'admin@example.com' && $password === 'admin') {
|
||||||
|
$_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>
|
||||||
@ -14,35 +14,24 @@ class MailService
|
|||||||
{
|
{
|
||||||
$cfg = self::loadConfig();
|
$cfg = self::loadConfig();
|
||||||
|
|
||||||
$autoload = __DIR__ . '/../vendor/autoload.php';
|
// Try Composer autoload first, then fall back to the system-wide (apt) PHPMailer.
|
||||||
if (file_exists($autoload)) {
|
$composerAutoload = __DIR__ . '/../vendor/autoload.php';
|
||||||
require_once $autoload;
|
if (file_exists($composerAutoload)) {
|
||||||
}
|
require_once $composerAutoload;
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
} elseif (file_exists('/usr/share/php/libphp-phpmailer/autoload.php')) {
|
||||||
@require_once 'libphp-phpmailer/autoload.php';
|
require_once '/usr/share/php/libphp-phpmailer/autoload.php';
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
@require_once 'libphp-phpmailer/src/Exception.php';
|
|
||||||
@require_once 'libphp-phpmailer/src/SMTP.php';
|
|
||||||
@require_once 'libphp-phpmailer/src/PHPMailer.php';
|
|
||||||
}
|
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
@require_once 'PHPMailer/src/Exception.php';
|
|
||||||
@require_once 'PHPMailer/src/SMTP.php';
|
|
||||||
@require_once 'PHPMailer/src/PHPMailer.php';
|
|
||||||
}
|
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
@require_once 'PHPMailer/Exception.php';
|
|
||||||
@require_once 'PHPMailer/SMTP.php';
|
|
||||||
@require_once 'PHPMailer/PHPMailer.php';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) {
|
||||||
return [ 'success' => false, 'error' => 'PHPMailer not available' ];
|
return [ 'success' => false, 'error' => 'PHPMailer not available' ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
||||||
try {
|
try {
|
||||||
|
$mail->SMTPDebug = 2;
|
||||||
|
$mail->Debugoutput = function($str, $level) {
|
||||||
|
file_put_contents(__DIR__ . '/mail.log', $str, FILE_APPEND);
|
||||||
|
};
|
||||||
$mail->isSMTP();
|
$mail->isSMTP();
|
||||||
$mail->Host = $cfg['smtp_host'] ?? '';
|
$mail->Host = $cfg['smtp_host'] ?? '';
|
||||||
$mail->Port = (int)($cfg['smtp_port'] ?? 587);
|
$mail->Port = (int)($cfg['smtp_port'] ?? 587);
|
||||||
@ -54,7 +43,7 @@ class MailService
|
|||||||
$mail->Username = $cfg['smtp_user'] ?? '';
|
$mail->Username = $cfg['smtp_user'] ?? '';
|
||||||
$mail->Password = $cfg['smtp_pass'] ?? '';
|
$mail->Password = $cfg['smtp_pass'] ?? '';
|
||||||
|
|
||||||
$fromEmail = $opts['from_email'] ?? ($cfg['from_email'] ?? 'no-reply@localhost');
|
$fromEmail = $opts['from_email'] ?? ($cfg['from_email'] ?? 'support@flatlogic.com');
|
||||||
$fromName = $opts['from_name'] ?? ($cfg['from_name'] ?? 'App');
|
$fromName = $opts['from_name'] ?? ($cfg['from_name'] ?? 'App');
|
||||||
$mail->setFrom($fromEmail, $fromName);
|
$mail->setFrom($fromEmail, $fromName);
|
||||||
if (!empty($opts['reply_to']) && filter_var($opts['reply_to'], FILTER_VALIDATE_EMAIL)) {
|
if (!empty($opts['reply_to']) && filter_var($opts['reply_to'], FILTER_VALIDATE_EMAIL)) {
|
||||||
@ -118,31 +107,12 @@ class MailService
|
|||||||
{
|
{
|
||||||
$cfg = self::loadConfig();
|
$cfg = self::loadConfig();
|
||||||
|
|
||||||
// Try Composer autoload if available (for PHPMailer)
|
// Try Composer autoload first, then fall back to the system-wide (apt) PHPMailer.
|
||||||
$autoload = __DIR__ . '/../vendor/autoload.php';
|
$composerAutoload = __DIR__ . '/../vendor/autoload.php';
|
||||||
if (file_exists($autoload)) {
|
if (file_exists($composerAutoload)) {
|
||||||
require_once $autoload;
|
require_once $composerAutoload;
|
||||||
}
|
} elseif (file_exists('/usr/share/php/libphp-phpmailer/autoload.php')) {
|
||||||
// Fallback to system-wide PHPMailer (installed via apt: libphp-phpmailer)
|
require_once '/usr/share/php/libphp-phpmailer/autoload.php';
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
// Debian/Ubuntu package layout (libphp-phpmailer)
|
|
||||||
@require_once 'libphp-phpmailer/autoload.php';
|
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
@require_once 'libphp-phpmailer/src/Exception.php';
|
|
||||||
@require_once 'libphp-phpmailer/src/SMTP.php';
|
|
||||||
@require_once 'libphp-phpmailer/src/PHPMailer.php';
|
|
||||||
}
|
|
||||||
// Alternative layout (older PHPMailer package names)
|
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
@require_once 'PHPMailer/src/Exception.php';
|
|
||||||
@require_once 'PHPMailer/src/SMTP.php';
|
|
||||||
@require_once 'PHPMailer/src/PHPMailer.php';
|
|
||||||
}
|
|
||||||
if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
|
|
||||||
@require_once 'PHPMailer/Exception.php';
|
|
||||||
@require_once 'PHPMailer/SMTP.php';
|
|
||||||
@require_once 'PHPMailer/PHPMailer.php';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$transport = $cfg['transport'] ?? 'smtp';
|
$transport = $cfg['transport'] ?? 'smtp';
|
||||||
@ -158,6 +128,10 @@ class MailService
|
|||||||
{
|
{
|
||||||
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
||||||
try {
|
try {
|
||||||
|
$mail->SMTPDebug = 2;
|
||||||
|
$mail->Debugoutput = function($str, $level) {
|
||||||
|
file_put_contents(__DIR__ . '/mail.log', $str, FILE_APPEND);
|
||||||
|
};
|
||||||
$mail->isSMTP();
|
$mail->isSMTP();
|
||||||
$mail->Host = $cfg['smtp_host'] ?? '';
|
$mail->Host = $cfg['smtp_host'] ?? '';
|
||||||
$mail->Port = (int)($cfg['smtp_port'] ?? 587);
|
$mail->Port = (int)($cfg['smtp_port'] ?? 587);
|
||||||
@ -169,7 +143,7 @@ class MailService
|
|||||||
$mail->Username = $cfg['smtp_user'] ?? '';
|
$mail->Username = $cfg['smtp_user'] ?? '';
|
||||||
$mail->Password = $cfg['smtp_pass'] ?? '';
|
$mail->Password = $cfg['smtp_pass'] ?? '';
|
||||||
|
|
||||||
$fromEmail = $cfg['from_email'] ?? 'no-reply@localhost';
|
$fromEmail = $cfg['from_email'] ?? 'support@flatlogic.com';
|
||||||
$fromName = $cfg['from_name'] ?? 'App';
|
$fromName = $cfg['from_name'] ?? 'App';
|
||||||
$mail->setFrom($fromEmail, $fromName);
|
$mail->setFrom($fromEmail, $fromName);
|
||||||
|
|
||||||
|
|||||||
@ -40,15 +40,15 @@ load_dotenv_if_needed([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$transport = env_val('MAIL_TRANSPORT', 'smtp');
|
$transport = env_val('MAIL_TRANSPORT', 'smtp');
|
||||||
$smtp_host = env_val('SMTP_HOST');
|
$smtp_host = env_val('SMTP_HOST', 'email-smtp.us-east-1.amazonaws.com');
|
||||||
$smtp_port = (int) env_val('SMTP_PORT', 587);
|
$smtp_port = (int) env_val('SMTP_PORT', 587);
|
||||||
$smtp_secure = env_val('SMTP_SECURE', 'tls'); // tls | ssl | null
|
$smtp_secure = env_val('SMTP_SECURE', 'tls'); // tls | ssl | null
|
||||||
$smtp_user = env_val('SMTP_USER');
|
$smtp_user = env_val('SMTP_USER', 'AKIAVEW7G4PQUBGM52OF');
|
||||||
$smtp_pass = env_val('SMTP_PASS');
|
$smtp_pass = env_val('SMTP_PASS', 'BLnD4hKGb6YkSz3gaQrf8fnyLi3C3/EdjOOsLEDTDPTz');
|
||||||
|
|
||||||
$from_email = env_val('MAIL_FROM', 'no-reply@localhost');
|
$from_email = env_val('MAIL_FROM', 'app@flatlogic.app');
|
||||||
$from_name = env_val('MAIL_FROM_NAME', 'App');
|
$from_name = env_val('MAIL_FROM_NAME', 'Flatlogic App');
|
||||||
$reply_to = env_val('MAIL_REPLY_TO');
|
$reply_to = env_val('MAIL_REPLY_TO', 'app@flatlogic.app');
|
||||||
|
|
||||||
$dkim_domain = env_val('DKIM_DOMAIN');
|
$dkim_domain = env_val('DKIM_DOMAIN');
|
||||||
$dkim_selector = env_val('DKIM_SELECTOR');
|
$dkim_selector = env_val('DKIM_SELECTOR');
|
||||||
|
|||||||
24859
mail/mail.log
Normal file
204
register.php
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
require_once 'mail/MailService.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// --- Helper function to fetch webinar details ---
|
||||||
|
function get_webinar_details($id) {
|
||||||
|
if (empty($id)) return null;
|
||||||
|
try {
|
||||||
|
$stmt = db()->prepare("SELECT id, title, description, scheduled_at, presenter FROM webinars WHERE id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
return $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("Database error fetching webinar ID $id: " . $e->getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Only allow POST requests ---
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Invalid request method.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$webinar_id = filter_input(INPUT_POST, 'webinar_id', FILTER_VALIDATE_INT) ?: 1;
|
||||||
|
$webinar = get_webinar_details($webinar_id);
|
||||||
|
|
||||||
|
if (!$webinar) {
|
||||||
|
http_response_code(404);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Webinar not found.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DATA CAPTURE ---
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$first_name = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_STRING);
|
||||||
|
$last_name = filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_STRING);
|
||||||
|
$company = filter_input(INPUT_POST, 'company', FILTER_SANITIZE_STRING);
|
||||||
|
$how_did_you_hear = filter_input(INPUT_POST, 'how_did_you_hear', FILTER_SANITIZE_STRING);
|
||||||
|
$timezone = filter_input(INPUT_POST, 'timezone', FILTER_SANITIZE_STRING);
|
||||||
|
|
||||||
|
// --- VALIDATION ---
|
||||||
|
if (!$first_name || !$last_name || !$email) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Please fill out all required fields.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// --- CHECK IF ALREADY REGISTERED OR SOFT-DELETED -- -
|
||||||
|
$stmt = db()->prepare("SELECT id, deleted_at FROM attendees WHERE webinar_id = ? AND email = ?");
|
||||||
|
$stmt->execute([$webinar_id, $email]);
|
||||||
|
$existing_user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$send_email = false;
|
||||||
|
|
||||||
|
if ($existing_user) {
|
||||||
|
if ($existing_user['deleted_at'] !== null) {
|
||||||
|
// --- USER IS SOFT-DELETED, SO REACTIVATE AND UPDATE ---
|
||||||
|
$sql = "UPDATE attendees SET first_name = ?, last_name = ?, company = ?, how_did_you_hear = ?, timezone = ?, deleted_at = NULL, consented = 1 WHERE id = ?";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$first_name, $last_name, $company, $how_did_you_hear, $timezone, $existing_user['id']]);
|
||||||
|
$send_email = true;
|
||||||
|
} else {
|
||||||
|
// --- USER IS ACTIVE, SO REJECT ---
|
||||||
|
echo json_encode(['success' => false, 'error' => 'You are already registered for this webinar.']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// --- REGISTER NEW USER ---
|
||||||
|
$password_hash = password_hash($email . time(), PASSWORD_DEFAULT);
|
||||||
|
$sql = "INSERT INTO attendees (webinar_id, first_name, last_name, email, company, how_did_you_hear, password, timezone)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
$stmt = db()->prepare($sql);
|
||||||
|
$stmt->execute([$webinar_id, $first_name, $last_name, $email, $company, $how_did_you_hear, $password_hash, $timezone]);
|
||||||
|
$send_email = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($send_email) {
|
||||||
|
// --- SEND CONFIRMATION EMAIL ---
|
||||||
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
|
||||||
|
$host = $_SERVER['HTTP_HOST'];
|
||||||
|
$logo_url = $protocol . '://' . $host . '/assets/pasted-20251030-095744-1b7c02ab.png';
|
||||||
|
$subject = "Confirmation: You're Registered for Building Scalable Apps with AppWizzy";
|
||||||
|
$webinar_date_obj = new DateTime('2025-11-19 10:00:00', new DateTimeZone('America/New_York'));
|
||||||
|
|
||||||
|
// --- PREPARE CALENDAR LINK ---
|
||||||
|
$event_title_cal = 'Building Scalable Apps with AppWizzy';
|
||||||
|
$event_description_cal = 'Professional Vibe-Coding Webinar. Join us to learn the fastest way to go from an idea to a working app you own.';
|
||||||
|
|
||||||
|
// Use the same webinar date as in the email body
|
||||||
|
$webinar_date_for_cal = new DateTime('2025-11-19 10:00:00', new DateTimeZone('America/New_York'));
|
||||||
|
|
||||||
|
// Convert to UTC for Google Calendar
|
||||||
|
$webinar_date_for_cal->setTimezone(new DateTimeZone('UTC'));
|
||||||
|
$start_time_utc_cal = $webinar_date_for_cal->format('Ymd\THis\Z');
|
||||||
|
|
||||||
|
// Add 1 hour for the end time
|
||||||
|
$webinar_date_for_cal->add(new DateInterval('PT1H'));
|
||||||
|
$end_time_utc_cal = $webinar_date_for_cal->format('Ymd\THis\Z');
|
||||||
|
|
||||||
|
$google_link = 'https://www.google.com/calendar/render?action=TEMPLATE' .
|
||||||
|
'&text=' . urlencode($event_title_cal) .
|
||||||
|
'&dates=' . $start_time_utc_cal . '/' . $end_time_utc_cal .
|
||||||
|
'&details=' . urlencode($event_description_cal) .
|
||||||
|
'&location=' . urlencode('Online') .
|
||||||
|
'&ctz=UTC';
|
||||||
|
|
||||||
|
$body_html = <<<HTML
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Webinar Registration Confirmation</title>
|
||||||
|
</head>
|
||||||
|
<body style="font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f4f4f4; color: #333; margin: 0; padding: 0;">
|
||||||
|
<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f4f4f4;">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<table width="600" border="0" cellspacing="0" cellpadding="0" style="background-color: #ffffff; margin: 20px auto; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
|
||||||
|
<!-- Header -->
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 40px 20px; background: linear-gradient(135deg, #1a237e 0%, #673ab7 100%); border-radius: 10px 10px 0 0;">
|
||||||
|
<img src="{$logo_url}" alt="AppWizzy Logo" style="height: 60px; margin-bottom: 20px;">
|
||||||
|
<h1 style="color: #ffffff; font-size: 28px; margin: 0; line-height: 1.2;">You're Registered!</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!-- Body -->
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 40px 30px;">
|
||||||
|
<h2 style="font-size: 22px; color: #1a237e;">Hello {$first_name},</h2>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6;">Thank you for registering for the <strong>Building Scalable Apps with AppWizzy</strong> webinar.</p>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6;">We're excited to have you join us for this professional vibe-coding session.</p>
|
||||||
|
<div style="background-color: #f9f9f9; border-left: 4px solid #673ab7; padding: 15px 20px; margin: 20px 0;">
|
||||||
|
<p style="margin: 0; font-size: 16px;"><strong>Webinar Details:</strong></p>
|
||||||
|
<p style="margin: 10px 0 0; font-size: 16px;">{$webinar_date_obj->format('l, F j, Y')} | <strong>4PM CET</strong> | {$webinar_date_obj->format('g:i A T')}</p>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6;">You'll learn 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>
|
||||||
|
<p style="font-size: 16px; line-height: 1.6;"><strong>Your personal webinar access link will be emailed to you 1 day before the event.</strong></p>
|
||||||
|
<p style="text-align: center; margin-top: 30px;">
|
||||||
|
<a href="{$google_link}" style="background: linear-gradient(135deg, #3f51b5 0%, #9c27b0 100%); color: #ffffff; padding: 12px 25px; text-decoration: none; border-radius: 8px; font-weight: bold;">Add to Calendar</a>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding: 20px; background-color: #f4f4f4; border-top: 1px solid #dddddd;">
|
||||||
|
<p style="margin: 0; color: #777;">© 2025 AppWizzy. All rights reserved.</p>
|
||||||
|
<p style="margin: 5px 0 0; color: #777;">You can <a href="{$protocol}://{$host}/" style="color: #3f51b5;">visit our website</a> for more information.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
HTML;
|
||||||
|
MailService::sendMail($email, $subject, $body_html);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- PREPARE SUCCESS RESPONSE ---
|
||||||
|
$webinar_date = new DateTime('2025-11-19 10:00:00', new DateTimeZone('America/New_York'));
|
||||||
|
$webinar_date->setTimezone(new DateTimeZone('UTC'));
|
||||||
|
$start_time_utc = $webinar_date->format('Ymd\THis\Z');
|
||||||
|
$webinar_date->add(new DateInterval('PT1H')); // Assume 1 hour duration
|
||||||
|
$end_time_utc = $webinar_date->format('Ymd\THis\Z');
|
||||||
|
|
||||||
|
$event_title = 'Building Scalable Apps with AppWizzy at 4PM CET';
|
||||||
|
$event_description = 'Professional Vibe-Coding Webinar\n\nJoin us for this webinar at 4PM CET | 10AM EST | 7AM PST. The fastest way to go from an idea to a working app you own, running on your server, with your database, using real frameworks.';
|
||||||
|
|
||||||
|
$google_link = 'https://www.google.com/calendar/render?action=TEMPLATE&text=' . urlencode($event_title) . '&dates=' . $start_time_utc . '/' . $end_time_utc . '&details=' . urlencode($event_description) . '&location=' . urlencode('Online') . '&ctz=UTC';
|
||||||
|
|
||||||
|
$ics_content = implode("\r\n", [
|
||||||
|
'BEGIN:VCALENDAR',
|
||||||
|
'VERSION:2.0',
|
||||||
|
'PRODID:-//Flatlogic//Building Scalable Apps with AppWizzy//EN',
|
||||||
|
'BEGIN:VEVENT',
|
||||||
|
'URL:' . 'http://' . $_SERVER['HTTP_HOST'],
|
||||||
|
'DTSTART:' . $start_time_utc,
|
||||||
|
'DTEND:' . $end_time_utc,
|
||||||
|
'SUMMARY:' . $event_title,
|
||||||
|
'DESCRIPTION:' . str_replace("\n", "\\n", $event_description),
|
||||||
|
'LOCATION:Online',
|
||||||
|
'END:VEVENT',
|
||||||
|
'END:VCALENDAR'
|
||||||
|
]);
|
||||||
|
$outlook_link = 'data:text/calendar;charset=utf-8,' . rawurlencode($ics_content);
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'webinar_title' => 'Building Scalable Apps with AppWizzy<br><small><strong>Professional Vibe-Coding Webinar</strong></small>',
|
||||||
|
'google_link' => $google_link,
|
||||||
|
'outlook_link' => $outlook_link
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'An unexpected server error occurred. Please try again.']);
|
||||||
|
}
|
||||||