Code restructure push

This commit is contained in:
twotalesanimation
2025-12-04 15:09:44 +02:00
parent 86faad7a78
commit be2b757f4e
111 changed files with 17297 additions and 19420 deletions

View File

@@ -0,0 +1,95 @@
<?php include_once('../config/connection.php');
include_once('../config/functions.php');
require_once("../config/env.php");
session_start();
$user_id = $_SESSION['user_id'] ?? null;
// CSRF Token Validation
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
http_response_code(403);
die('Security token validation failed. Please try again.');
}
// campsites.php
$conn = openDatabaseConnection();
// Get text inputs
$name = validateName($_POST['name'] ?? '') ?: '';
$desc = isset($_POST['description']) ? htmlspecialchars($_POST['description'], ENT_QUOTES, 'UTF-8') : '';
$lat = isset($_POST['latitude']) ? floatval($_POST['latitude']) : 0.0;
$lng = isset($_POST['longitude']) ? floatval($_POST['longitude']) : 0.0;
$website = isset($_POST['website']) ? filter_var($_POST['website'], FILTER_VALIDATE_URL) : '';
$telephone = validatePhoneNumber($_POST['telephone'] ?? '') ?: '';
if (empty($name)) {
http_response_code(400);
die('Campsite name is required.');
}
// Handle file upload
$thumbnailPath = null;
if (isset($_FILES['thumbnail']) && $_FILES['thumbnail']['error'] !== UPLOAD_ERR_NO_FILE) {
// Validate file using hardened validation function
$validationResult = validateFileUpload($_FILES['thumbnail'], 'profile_picture');
if ($validationResult === false) {
http_response_code(400);
die('Invalid thumbnail image. Only JPG, JPEG, PNG, GIF, and WEBP images under 5MB are allowed.');
}
$uploadDir = "assets/uploads/campsites/";
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
if (!is_writable($uploadDir)) {
http_response_code(500);
die('Upload directory is not writable.');
}
$randomFilename = $validationResult['filename'];
$targetFile = $uploadDir . $randomFilename;
if (move_uploaded_file($_FILES["thumbnail"]["tmp_name"], $targetFile)) {
chmod($targetFile, 0644);
$thumbnailPath = $targetFile;
} else {
http_response_code(500);
die('Failed to move uploaded file.');
}
}
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
if ($id > 0) {
// UPDATE
if ($thumbnailPath) {
$stmt = $conn->prepare("UPDATE campsites SET name=?, description=?, latitude=?, longitude=?, website=?, telephone=?, thumbnail=? WHERE id=?");
$stmt->bind_param("ssddsssi", $name, $desc, $lat, $lng, $website, $telephone, $thumbnailPath, $id);
} else {
$stmt = $conn->prepare("UPDATE campsites SET name=?, description=?, latitude=?, longitude=?, website=?, telephone=? WHERE id=?");
$stmt->bind_param("ssddssi", $name, $desc, $lat, $lng, $website, $telephone, $id);
}
// Log the action
auditLog($user_id, 'CAMPSITE_UPDATE', 'campsites', $id, ['name' => $name]);
} else {
// INSERT
$stmt = $conn->prepare("INSERT INTO campsites (name, description, latitude, longitude, website, telephone, thumbnail, user_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("ssddsssi", $name, $desc, $lat, $lng, $website, $telephone, $thumbnailPath, $user_id);
// Log the action
auditLog($user_id, 'CAMPSITE_CREATE', 'campsites', 0, ['name' => $name]);
}
if (!$stmt->execute()) {
http_response_code(500);
die('Database error: ' . $stmt->error);
}
$stmt->close();
header("Location: campsites.php");
?>

View File

@@ -0,0 +1,227 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.trip-booking {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const tables = document.querySelectorAll("table");
tables.forEach((table) => {
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = table.previousElementSibling;
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
if (rows.length === 0) {
filterInput.style.display = "none";
} else {
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
}
});
});
</script>
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Camping Bookings</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">Camping Bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<?php
echo "<div class='trip-booking' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<div style='padding:10px;'>";
echo "<h4>BASE4 Camping</h4>";
// Fetch bookings for the current trip
$bookingsSql = "SELECT b.user_id, b.from_date, b.to_date, b.num_vehicles, b.num_adults, b.num_children, b.add_firewood, b.status,
u.first_name, u.last_name,
(b.total_amount - b.discount_amount) AS paid
FROM bookings b
INNER JOIN users u ON b.user_id = u.user_id
WHERE b.booking_type = 'camping'";
$stmt = $conn->prepare($bookingsSql);
$stmt->execute();
$bookingsResult = $stmt->get_result();
if ($bookingsResult->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th>Name</th>
<th>From</th>
<th>To</th>
<th>Vehicles</th>
<th>Adults</th>
<th>Children</th>
<th>Add Firewood</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>';
while ($booking = $bookingsResult->fetch_assoc()) {
$userName = htmlspecialchars($booking['first_name'] . ' ' . $booking['last_name']);
$numVehicles = htmlspecialchars($booking['num_vehicles']);
$from = htmlspecialchars($booking['from_date']);
$to = htmlspecialchars($booking['to_date']);
$numAdults = htmlspecialchars($booking['num_adults']);
$numChildren = htmlspecialchars($booking['num_children']);
$radio = $booking['add_firewood'] == 1 ? "YES" : "NO";
$status = htmlspecialchars($booking['status']);
$paid = "R " . number_format($booking['paid'], 2);
echo "<tr>
<td>{$userName}</td>
<td>{$from}</td>
<td>{$to}</td>
<td>{$numVehicles}</td>
<td>{$numAdults}</td>
<td>{$numChildren}</td>
<td>{$radio}</td>
<td>{$status}</td>
<td>{$paid}</td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No bookings found for this trip.</p>';
}
echo "</div>";
echo "</div>";
?>
</div>
</section>
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,247 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
// Fetch all trips
$courseSql = "SELECT date, course_id, course_type FROM courses";
$courseResult = $conn->query($courseSql);
if (!$courseResult) {
echo "Error in SQL query: " . $conn->error;
}
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.trip-booking {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const tables = document.querySelectorAll("table");
tables.forEach((table) => {
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = table.previousElementSibling;
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
if (rows.length === 0) {
filterInput.style.display = "none";
} else {
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
}
});
});
</script>
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Course Bookings</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">Course Bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<?php
if ($courseResult->num_rows > 0) {
while ($course = $courseResult->fetch_assoc()) {
$course_id = $course['course_id'];
$date = $course['date'];
$type = htmlspecialchars($course['course_type']);
if ($type === "driver_training") {
$course_name = "Basic 4X4 Driver Training Course ".$date;
} elseif ($type === "bush_mechanics") {
$course_name = "Bush Mechanics Course ".$date;
} elseif ($type === "rescue_recovery") {
$course_name = "Rescue & Recovery Training Course ".$date;
} else {
$course_name = "General Course ".$date; // Default fallback description
}
echo "<div class='trip-booking' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<div style='padding:10px;'>";
echo "<h4>{$course_name}</h4>";
// Fetch bookings for the current trip
$bookingsSql = "SELECT b.user_id, b.num_adults, b.total_amount, b.status, b.course_non_members,
u.first_name, u.last_name, u.profile_pic
FROM bookings b
INNER JOIN users u ON b.user_id = u.user_id
WHERE b.course_id = ?";
if ($stmt = $conn->prepare($bookingsSql)) {
$stmt->bind_param('i', $course_id);
$stmt->execute();
$bookingsResult = $stmt->get_result();
} else {
echo "Error in prepared statement: " . $conn->error;
}
if ($bookingsResult->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Members</th>
<th>Non-Members</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>';
while ($booking = $bookingsResult->fetch_assoc()) {
$userName = htmlspecialchars($booking['first_name'] . ' ' . $booking['last_name']);
$members = htmlspecialchars($booking['num_adults']);
$non_members = htmlspecialchars($booking['course_non_members']);
$status = htmlspecialchars($booking['status']);
$paid = "R " . number_format($booking['total_amount'], 2);
echo "<tr>
<td><img src=".$booking['profile_pic']." alt='Profile Picture' class='profile-pic'></td>
<td>{$userName}</td>
<td>{$members}</td>
<td>{$non_members}</td>
<td>{$status}</td>
<td>{$paid}</td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No bookings found for this trip.</p>';
}
echo "</div>";
echo "</div>";
}
} else {
echo '<p>No courses found.</p>';
}
?>
</div>
</section>
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

227
src/admin/admin_efts.php Normal file
View File

@@ -0,0 +1,227 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.infobox {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
.theme-btn,
a.theme-btn {
padding: 0px 14px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.querySelector("table");
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = document.getElementById("filterInput");
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
});
</script>
<!-- Page Banner Start -->
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA EFT Payments</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">4WDCSA EFT Payments</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Tour List Area start -->
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class='infobox' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>
<div style='padding:10px;'>
<?php
// Fetch payments
$paymentSql = "SELECT b.user_id, b.eft_id, b.amount, b.status, b.timestamp, b.description,
u.first_name, u.last_name
FROM efts b
INNER JOIN users u ON b.user_id = u.user_id";
$stmt = $conn->prepare($paymentSql);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th>Date</th>
<th>Name</th>
<th>Description</th>
<th>Amount</th>
<th>Reference</th>
<th>Status</th>
</tr>
</thead>
<tbody>';
while ($row = $result->fetch_assoc()) {
// Generate a unique token for this EFT
echo "<tr>
<td>" . htmlspecialchars($row['timestamp']) . "</td>
<td>" . htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) . "</td>
<td>" . htmlspecialchars($row['description']) . "</td>
<td>" . htmlspecialchars($row['amount']) . "</td>
<td>" . htmlspecialchars($row['eft_id']) . "</td>";
if (($row['status']) == 'AWAITING PAYMENT') {
echo "<td><a href='process_eft.php?token=" . encryptData($row['eft_id'], $salt) . "' class='theme-btn style-two style-three'>
<span data-hover='PAYMENT RECEIVED'>" . htmlspecialchars($row['status']) . "</span>
</a></td></tr>";
} elseif (($row['status']) == 'PROCESSING') {
echo "<td><a href='process_payments.php' class='theme-btn style-two style-three'>
<span data-hover='PROCESS'>PROCESS</span>
</a></td></tr>";
} else {
echo "<td>" . htmlspecialchars($row['status']) . "</td>";
}
}
} else {
echo '<tr><td colspan="5">No records found</td></tr>';
} ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

238
src/admin/admin_members.php Normal file
View File

@@ -0,0 +1,238 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['accept_indemnity'])) {
$user_id = intval($_POST['user_id']);
$stmt = $conn->prepare("UPDATE membership_application SET accept_indemnity = 1 WHERE user_id = ?");
if ($stmt) {
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->close();
}
}
// SQL query to fetch membership applications
$stmt = $conn->prepare("SELECT user_id, first_name, last_name, tel_cell, email, dob, accept_indemnity FROM membership_application");
$stmt->execute();
$result = $stmt->get_result();
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.infobox {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
.theme-btn,
a.theme-btn {
padding: 0px 14px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.querySelector("table");
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = document.getElementById("filterInput");
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
});
</script>
<!-- Page Banner Start -->
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Members</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">4WDCSA Members</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Tour List Area start -->
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class='infobox' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>
<div style='padding:10px;'>
<input type="text" id="filterInput" class="filter-input" placeholder="Filter results...">
<table>
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Cell Number</th>
<th>Email</th>
<th>Date of Birth</th>
<th>Membership</th>
<th>View Info</th>
<th>Indemnity</th>
</tr>
</thead>
<tbody>
<?php
if ($result->num_rows > 0) {
// Output data of each row
while ($row = $result->fetch_assoc()) {
echo "<tr>
<td>" . htmlspecialchars($row['first_name']) . "</td>
<td>" . htmlspecialchars($row['last_name']) . "</td>
<td>" . htmlspecialchars($row['tel_cell']) . "</td>
<td>" . htmlspecialchars($row['email']) . "</td>
<td>" . htmlspecialchars($row['dob']) . "</td>
<td>" . (getUserMemberStatus($row['user_id']) ? 'ACTIVE' : 'INACTIVE') . "</td>
<td><a href='member_info.php?token=" . encryptData($row['user_id'], $salt) . "' class='theme-btn style-two style-three'><span data-hover='PAYMENT RECEIVED'>View Info</span></a></td>
<td>";
if (!$row['accept_indemnity']) {
echo "<form method='POST' style='display:inline;'>
<input type='hidden' name='user_id' value='" . $row['user_id'] . "'>
<button type='submit' name='accept_indemnity' class='theme-btn small'>Accept</button>
</form>";
} else {
echo "✅ Accepted";
}
echo "</td>
</tr>";
}
} else {
echo '<tr><td colspan="8">No records found</td></tr>';
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,211 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.infobox {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.querySelector("table");
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = document.getElementById("filterInput");
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
});
</script>
<!-- Page Banner Start -->
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Payments</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">4WDCSA Payments</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Tour List Area start -->
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class='infobox' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>
<div style='padding:10px;'>
<?php
// Fetch payments
$paymentSql = "SELECT b.user_id, b.payment_id, b.amount, b.status, b.date, b.description,
u.first_name, u.last_name
FROM payments b
INNER JOIN users u ON b.user_id = u.user_id";
$stmt = $conn->prepare($paymentSql);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th>Date</th>
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>Amount</th>
<th>Status</th>
</tr>
</thead>
<tbody>';
while ($row = $result->fetch_assoc()) {
echo "<tr>
<td>" . htmlspecialchars($row['date']) . "</td>
<td>" . htmlspecialchars($row['payment_id']) . "</td>
<td>" . htmlspecialchars($row['first_name'] . ' ' . $row['last_name']) . "</td>
<td>" . htmlspecialchars($row['description']) . "</td>
<td>" . htmlspecialchars($row['amount']) . "</td>
<td>" . htmlspecialchars($row['status']) . "</td>
</tr>";
}
} else {
echo '<tr><td colspan="5">No records found</td></tr>';
} ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,240 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
// Fetch all trips
$tripsSql = "SELECT trip_id, trip_name FROM trips";
$tripsResult = $conn->query($tripsSql);
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.trip-booking {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const tables = document.querySelectorAll("table");
tables.forEach((table) => {
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = table.previousElementSibling;
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
if (rows.length === 0) {
filterInput.style.display = "none";
} else {
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
}
});
});
</script>
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Trip Bookings</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">Trip Bookings</li>
</ol>
</nav>
</div>
</div>
</section>
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<?php
if ($tripsResult->num_rows > 0) {
while ($trip = $tripsResult->fetch_assoc()) {
$tripId = $trip['trip_id'];
$tripName = htmlspecialchars($trip['trip_name']);
echo "<div class='trip-booking' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<div style='padding:10px;'>";
echo "<h4>{$tripName}</h4>";
// Fetch bookings for the current trip
$bookingsSql = "SELECT b.user_id, b.num_vehicles, b.num_adults, b.num_children, b.num_pensioners, b.radio, b.status,
u.first_name, u.last_name,
(b.total_amount - b.discount_amount) AS paid
FROM bookings b
INNER JOIN users u ON b.user_id = u.user_id
WHERE b.trip_id = ?";
$stmt = $conn->prepare($bookingsSql);
$stmt->bind_param('i', $tripId);
$stmt->execute();
$bookingsResult = $stmt->get_result();
if ($bookingsResult->num_rows > 0) {
echo '<input type="text" class="filter-input" placeholder="Filter results...">';
echo '<table>
<thead>
<tr>
<th></th>
<th>Name</th>
<th>Vehicles</th>
<th>Adults</th>
<th>Children</th>
<th>Pensioners</th>
<th>Radio</th>
<th>Status</th>
<th>Amount</th>
</tr>
</thead>
<tbody>';
while ($booking = $bookingsResult->fetch_assoc()) {
$userName = htmlspecialchars($booking['first_name'] . ' ' . $booking['last_name']);
$numVehicles = htmlspecialchars($booking['num_vehicles']);
$numAdults = htmlspecialchars($booking['num_adults']);
$numPensioners = htmlspecialchars($booking['num_pensioners']);
$numChildren = htmlspecialchars($booking['num_children']);
$radio = $booking['radio'] == 1 ? "YES" : "NO";
$status = htmlspecialchars($booking['status']);
$paid = "R " . number_format($booking['paid'], 2);
echo "<tr>
<td><img src=".$booking['profile_pic']." alt='Profile Picture' class='profile-pic'></td>
<td>{$userName}</td>
<td>{$numVehicles}</td>
<td>{$numAdults}</td>
<td>{$numChildren}</td>
<td>{$numPensioners}</td>
<td>{$radio}</td>
<td>{$status}</td>
<td>{$paid}</td>
</tr>";
}
echo '</tbody></table>';
} else {
echo '<p>No bookings found for this trip.</p>';
}
echo "</div>";
echo "</div>";
}
} else {
echo '<p>No trips found.</p>';
}
?>
</div>
</section>
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,204 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
// SQL query to fetch data
$sql = "SELECT ip_address, user_id, page_url, referrer_url, visit_time, country FROM visitor_logs WHERE NOT (ip_address = '185.203.122.69' OR ip_address = '156.155.29.213') ORDER BY visit_time DESC";
$result = $conn->query($sql);
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.infobox {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.querySelector("table");
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = document.getElementById("filterInput");
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
});
</script>
<!-- Page Banner Start -->
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Visitor Logs</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">4WDCSA Visitor Logs</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Tour List Area start -->
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class='infobox' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>
<div style='padding:10px;'>
<input type="text" id="filterInput" class="filter-input" placeholder="Filter results...">
<table>
<thead>
<tr>
<th>Country</th>
<th>IP Address</th>
<th>User ID</th>
<th>Page URL</th>
<th>Referrer</th>
<th>Timestamp</th>
</tr>
</thead>
<tbody>
<?php
if ($result->num_rows > 0) {
while ($row = $result->fetch_assoc()) {
echo "<tr>
<td>" . ($row['country']) . "</td>
<td>" . htmlspecialchars($row['ip_address']) . "</td>
<td>" . ($row['user_id'] !== null ? htmlspecialchars(getFullName($row['user_id'])) : '-') . "</td>
<td>" . htmlspecialchars($row['page_url']) . "</td>
<td>" . ($row['referrer_url'] ? htmlspecialchars($row['referrer_url']) : '-') . "</td>
<td>" . htmlspecialchars($row['visit_time']) . "</td>
</tr>";
}
} else {
echo '<tr><td colspan="5">No logs found</td></tr>';
}
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,284 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(__DIR__));
include_once($rootPath . '/header.php');
checkAdmin();
// SQL query to fetch data
$sql = "SELECT user_id, first_name, last_name, email, member, date_joined, token, is_verified, profile_pic FROM users";
$result = $conn->query($sql);
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.infobox {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const table = document.querySelector("table");
const headers = table.querySelectorAll("thead th");
const rows = Array.from(table.querySelectorAll("tbody tr"));
const filterInput = document.getElementById("filterInput");
headers.forEach((header, index) => {
header.addEventListener("click", () => {
const sortedRows = rows.sort((a, b) => {
const aText = a.cells[index].textContent.trim().toLowerCase();
const bText = b.cells[index].textContent.trim().toLowerCase();
if (aText < bText) return -1;
if (aText > bText) return 1;
return 0;
});
if (header.classList.contains("asc")) {
header.classList.remove("asc");
header.classList.add("desc");
sortedRows.reverse();
} else {
headers.forEach(h => h.classList.remove("asc", "desc"));
header.classList.add("asc");
}
const tbody = table.querySelector("tbody");
tbody.innerHTML = "";
sortedRows.forEach(row => tbody.appendChild(row));
});
});
filterInput.addEventListener("input", function() {
const filterValue = filterInput.value.trim().toLowerCase();
rows.forEach(row => {
const rowText = row.textContent.trim().toLowerCase();
row.style.display = rowText.includes(filterValue) ? "" : "none";
});
});
});
</script>
<!-- Page Banner Start -->
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">4WDCSA Site Users</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index.php">Home</a></li>
<li class="breadcrumb-item active">4WDCSA Site Users</li>
</ol>
</nav>
</div>
</div>
</section>
<?php if (isset($_SESSION['message'])): ?>
<div class="alert alert-warning message-box">
<?php echo $_SESSION['message']; ?>
<span class="close-btn" onclick="this.parentElement.style.display='none'">&times;</span>
</div>
<?php unset($_SESSION['message']); ?>
<?php endif; ?>
<!-- Tour List Area start -->
<section class="tour-list-page py-10 rel z-1">
<div class="container">
<div class="row">
<div id="response-message" style="margin-top: 1rem;"></div>
<div class="col-lg-12">
<div class='infobox' data-aos='fade-up' data-aos-duration='1500' data-aos-offset='50'>
<div style='padding:10px;'>
<input type="text" id="filterInput" class="filter-input" placeholder="Filter results...">
<table>
<thead>
<tr>
<th></th>
<!-- <th></th> -->
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Member</th>
<th>Indemnity</th>
<th>Date Joined</th>
<th>Verified</th>
<th></th>
</tr>
</thead>
<tbody>
<?php
if ($result->num_rows > 0) {
// Output data of each row
while ($row = $result->fetch_assoc()) {
if (getUserMemberStatus($row['user_id'])) {
$member = "\u{2713}";
} else {
$member = "\u{2717}";
}
$indemnityPending = false;
$userId = $row['user_id'];
$stmt = $conn->prepare("SELECT user_id FROM membership_application WHERE user_id = ? AND accept_indemnity = 0 LIMIT 1");
$stmt->bind_param("i", $userId);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
$indemnityPending = true;
}
$stmt->close();
echo "<tr>
<td><img src=" . $row['profile_pic'] . " alt='Profile Picture' class='profile-pic'></td>
<td>" . htmlspecialchars($row['first_name']) . "</td>
<td>" . htmlspecialchars($row['last_name']) . "</td>
<td>" . htmlspecialchars($row['email']) . "</td>
<td>" . $member . "</td>
<td>" . $indemnityPending . "</td>
<td>" . htmlspecialchars($row['date_joined']) . "</td>
<td>";
if ($row['is_verified'] != 1) {
echo "
<button class='resend-btn'
data-email=" . htmlspecialchars($row['email'] ?? '') . "
data-name=" . htmlspecialchars($row['first_name'] ?? '') . " " . htmlspecialchars($row['last_name'] ?? '') . "
data-token=" . htmlspecialchars($row['token'] ?? '') . ">
Resend Email
</button>";
} else {
echo "\u{2713}";
}
// echo "</td>
// <td><a href='linkmembership.php?user_id=".$row['user_id']."'>Link Membership</a></td>
// </tr>";
}
} else {
echo '<tr><td colspan="5">No records found</td></tr>';
} ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<script>
document.querySelectorAll('.resend-btn').forEach(button => {
button.addEventListener('click', function() {
const email = this.dataset.email;
const name = this.dataset.name;
const token = this.dataset.token;
fetch('resend_verification', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
name,
token
})
})
.then(response => response.json())
.then(data => {
const messageDiv = document.getElementById('response-message');
messageDiv.textContent = data.message;
messageDiv.style.color = data.success ? 'green' : 'red';
})
.catch(error => {
console.error('Error:', error);
});
});
});
</script>
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,36 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . "/src/config/session.php");
require_once($rootPath . "/src/config/connection.php");
require_once($rootPath . "/src/config/functions.php");
// Prepare the SQL query to fetch bar tabs along with user details, including user_id
$sql = "
SELECT bt.tab_id, u.user_id, u.first_name, u.last_name, u.profile_pic
FROM bar_tabs bt
JOIN users u ON bt.user_id = u.user_id
";
// Execute the query
$result = mysqli_query($conn, $sql);
// Check if there are results
if (mysqli_num_rows($result) > 0) {
// Create an array to hold the data
$barTabs = [];
// Fetch each row
while ($row = mysqli_fetch_assoc($result)) {
$barTabs[] = $row;
}
// Return the data as JSON
echo json_encode($barTabs);
} else {
echo json_encode([]);
}
// Close the database connection
mysqli_close($conn);
?>

30
src/api/fetch_drinks.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . "/src/config/connection.php");
if (isset($_GET['tab_id'])) {
$tab_id = (int) $_GET['tab_id']; // Convert to integer
if ($tab_id <= 0) {
echo json_encode(['status' => 'error', 'message' => 'Invalid tab ID.']);
exit();
}
// Fetch drinks available for this tab
$stmt = $conn->prepare("SELECT * FROM bar_items");
$stmt->execute();
$result = $stmt->get_result();
$drinks = [];
while ($row = $result->fetch_assoc()) {
$drinks[] = $row;
}
echo json_encode($drinks);
} else {
echo json_encode(['status' => 'error', 'message' => 'Tab ID is required.']);
}
?>

24
src/api/fetch_users.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . "/src/config/env.php");
require_once($rootPath . "/src/config/session.php");
require_once($rootPath . "/src/config/connection.php");
require_once($rootPath . "/src/config/functions.php");
if ($conn->connect_error) {
die(json_encode([])); // Return empty JSON on failure
}
$stmt = $conn->prepare("SELECT user_id, first_name, last_name FROM users ORDER BY first_name ASC");
$stmt->execute();
$result = $stmt->get_result();
$users = [];
while ($row = $result->fetch_assoc()) {
$users[] = $row;
}
echo json_encode($users);
$conn->close();
?>

39
src/api/get_campsites.php Normal file
View File

@@ -0,0 +1,39 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . "/src/config/env.php");
include_once('../config/connection.php');
include_once('../config/functions.php');
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT
c.*,
u.first_name,
u.last_name,
u.profile_pic
FROM campsites c
LEFT JOIN users u ON c.user_id = u.user_id");
$stmt->execute();
$result = $stmt->get_result();
$campsites = [];
while ($row = $result->fetch_assoc()) {
$campsites[] = [
'id' => $row['id'],
'name' => $row['name'],
'description' => $row['description'],
'website' => $row['website'],
'telephone' => $row['telephone'],
'latitude' => $row['latitude'],
'longitude' => $row['longitude'],
'thumbnail' => $row['thumbnail'],
'user' => [
'first_name' => $row['first_name'],
'last_name' => $row['last_name'],
'profile_pic' => $row['profile_pic']
]
];
}
header('Content-Type: application/json');
echo json_encode($campsites);

24
src/api/get_tab_total.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . "/src/config/env.php");
require_once($rootPath . "/src/config/connection.php");
if (isset($_POST['tab_id'])) {
$tab_id = (int) $_POST['tab_id']; // Ensure it's an integer
// Get the total from the bar_transactions table
$query = "SELECT SUM(item_price) AS total FROM bar_transactions WHERE tab_id = '$tab_id'";
$result = mysqli_query($conn, $query);
if ($result) {
$row = mysqli_fetch_assoc($result);
$total = $row['total'] ? $row['total'] : 0; // If no transactions, total is 0
echo json_encode(['status' => 'success', 'total' => $total]);
} else {
echo json_encode(['status' => 'error', 'message' => 'Failed to fetch total.']);
}
} else {
echo json_encode(['status' => 'error', 'message' => 'Missing tab ID.']);
}
?>

View File

@@ -0,0 +1,146 @@
<?php
$rootPath = dirname(dirname(__DIR__));
require_once($rootPath . "/src/config/env.php");
require_once($rootPath . "/src/config/session.php");
require_once($rootPath . "/src/config/connection.php");
require_once($rootPath . "/src/config/functions.php");
require_once($rootPath . '/google-client/vendor/autoload.php'); // Add this line for Google Client
// Check if connection is established
if (!$conn) {
json_encode(['status' => 'error', 'message' => 'Database connection failed.']);
exit();
}
// Google Client Setup
$client = new Google_Client();
$client->setClientId('948441222188-8qhboq2urr8o9n35mc70s5h2nhd52v0m.apps.googleusercontent.com');
$client->setClientSecret('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');
$client->setRedirectUri($_ENV['HOST'] . '/google_validate_login.php');
$client->addScope("email");
$client->addScope("profile");
// Check if Google login code is set
if (isset($_GET['code'])) {
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
if (!isset($token["error"])) {
$client->setAccessToken($token['access_token']);
$google_oauth = new Google_Service_Oauth2($client);
$google_account_info = $google_oauth->userinfo->get();
// Get user info from Google
$email = $google_account_info->email;
$name = $google_account_info->name;
$first_name = $google_account_info->given_name;
$last_name = $google_account_info->family_name;
$picture = $google_account_info->picture;
// Check if the user exists in the database
$query = "SELECT * FROM users WHERE email = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
// User does not exist, so register them
$password = null; // No password for Google login
$query = "INSERT INTO users (email, first_name, last_name, profile_pic, password, is_verified) VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($query);
$is_verified = 1; // Assuming Google users are considered verified
$stmt->bind_param("sssssi", $email, $first_name, $last_name, $picture, $password, $is_verified);
if ($stmt->execute()) {
// User successfully registered, set session and redirect
$_SESSION['user_id'] = $conn->insert_id;
$_SESSION['first_name'] = $first_name;
$_SESSION['profile_pic'] = $picture;
// echo json_encode(['status' => 'success', 'message' => 'Google login successful']);
header("Location: index.php");
exit();
} else {
// echo json_encode(['status' => 'error', 'message' => 'Failed to register user.']);
header("Location: index.php");
exit();
}
} else {
// User exists, set session and redirect
$row = $result->fetch_assoc();
$_SESSION['user_id'] = $row['user_id'];
$_SESSION['first_name'] = $row['first_name'];
$_SESSION['profile_pic'] = $row['profile_pic'];
// echo json_encode(['status' => 'success', 'message' => 'Google login successful']);
header("Location: index.php");
exit();
}
$stmt->close();
} else {
echo "Login failed.";
exit();
}
}
// Check if email and password login is requested
if (isset($_POST['email']) && isset($_POST['password'])) {
// Retrieve and sanitize form data
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
$password = trim($_POST['password']); // Remove extra spaces
// Validate input
if (empty($email) || empty($password)) {
echo json_encode(['status' => 'error', 'message' => 'Please enter both email and password.']);
exit();
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo json_encode(['status' => 'error', 'message' => 'Invalid email format.']);
exit();
}
// Prepare SQL statement to fetch user details
$query = "SELECT * FROM users WHERE email = ?";
$stmt = $conn->prepare($query);
if (!$stmt) {
echo json_encode(['status' => 'error', 'message' => 'Database query preparation failed.']);
exit();
}
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
// Check if user exists and verify password
if ($result->num_rows == 1) {
$row = $result->fetch_assoc();
// Check if the user is verified
if ($row['is_verified'] == 0) {
echo json_encode(['status' => 'error', 'message' => 'Your account is not verified. Please check your email for the verification link.']);
exit();
}
if (password_verify($password, $row['password'])) {
// Password is correct, set up session
$_SESSION['user_id'] = $row['user_id']; // Adjust as per your table structure
$_SESSION['first_name'] = $row['first_name']; // Adjust as per your table structure
$_SESSION['profile_pic'] = $row['profile_pic'];
echo json_encode(['status' => 'success', 'message' => 'Successful Login']);
} else {
// Password is incorrect
echo json_encode(['status' => 'error', 'message' => 'Invalid password.']);
}
} else {
// User does not exist
echo json_encode(['status' => 'error', 'message' => 'User with that email does not exist.']);
}
// Close the statement and connection
$stmt->close();
}
// Close connection
$conn->close();
exit();
?>

60
src/bootstrap.php Normal file
View File

@@ -0,0 +1,60 @@
<?php
/**
* Bootstrap - Central configuration loader
*
* All PHP files should include this file first to set up:
* - Path constants
* - Database connection
* - Session management
* - Core functions
*
* Usage:
* <?php require_once(__DIR__ . '/../../bootstrap.php'); ?>
*
* Then use constants:
* - APP_ROOT: Root directory
* - SRC_ROOT: src/ directory
* - CONFIG_PATH: src/config/ directory
* - CLASSES_PATH: src/classes/ directory
* - COMPONENTS_PATH: components/ directory
*
* And use globals:
* - $conn: MySQLi connection
* - $db: DatabaseService instance
*/
// Define root paths - adjust based on file location
if (!defined('APP_ROOT')) {
define('APP_ROOT', dirname(__DIR__));
}
if (!defined('SRC_ROOT')) {
define('SRC_ROOT', APP_ROOT . '/src');
}
if (!defined('CONFIG_PATH')) {
define('CONFIG_PATH', SRC_ROOT . '/config');
}
if (!defined('CLASSES_PATH')) {
define('CLASSES_PATH', SRC_ROOT . '/classes');
}
if (!defined('COMPONENTS_PATH')) {
define('COMPONENTS_PATH', APP_ROOT . '/components');
}
if (!defined('ASSETS_PATH')) {
define('ASSETS_PATH', APP_ROOT . '/assets');
}
// Load environment variables
require_once(CONFIG_PATH . '/env.php');
// Load database connection
require_once(CONFIG_PATH . '/connection.php');
// Load session management
require_once(CONFIG_PATH . '/session.php');
// Load core functions
require_once(CONFIG_PATH . '/functions.php');
// Optional: Set global timezone
date_default_timezone_set($_ENV['TIMEZONE'] ?? 'Africa/Johannesburg');
?>

27
src/config/connection.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
// Disable mysqli exceptions so we can handle connection errors gracefully
mysqli_report(MYSQLI_REPORT_OFF);
$dbhost = $_ENV['DB_HOST'];
$dbuser = $_ENV['DB_USER'];
$dbpass = $_ENV['DB_PASS'];
$dbname = $_ENV['DB_NAME'];
$salt = $_ENV['SALT'];
// echo "hello. ". $dbhost;
if(!$conn = @mysqli_connect($dbhost, $dbuser, $dbpass, $dbname)){
// Log the error to file instead of stderr (no red output)
@error_log("Database Connection Error: " . mysqli_connect_error(), 3, dirname(__DIR__) . "/logs/db_errors.log");
$conn = null;
$db = null;
} else {
date_default_timezone_set('Africa/Johannesburg');
// Initialize DatabaseService for modern queries
require_once(__DIR__ . '/../../classes/DatabaseService.php');
$db = new DatabaseService($conn);
}

6
src/config/env.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
require_once __DIR__ . '/../../vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
$dotenv->load();

2804
src/config/functions.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
<?php
require 'env.php';
require 'connection.php';
$conn = openDatabaseConnection();
if (!$conn) {
die('Database connection failed');
}
$sql = file_get_contents('migrations/001_phase1_security_schema.sql');
if ($conn->multi_query($sql)) {
echo "✓ Migration executed successfully\n";
} else {
echo "✗ Migration error: " . $conn->error . "\n";
}
$conn->close();
?>

15
src/config/session.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
session_start();
function Message(){
if(isset($_SESSION["message"])){
$output="<div class=\"message\">";
$output .= htmlentities($_SESSION['message']);
$output .="</div>";
$_SESSION['message']=null;
return $output;
}
}
?>

View File

@@ -0,0 +1,72 @@
<?php
$rootPath = dirname(dirname(dirname(__DIR__)));
require_once($rootPath . '/src/config/env.php');
require_once($rootPath . '/src/config/session.php');
require_once($rootPath . '/src/config/connection.php');
require_once($rootPath . '/src/config/functions.php');
$response = array('status' => 'error', 'message' => 'Something went wrong');
// Check if the user is logged in
if (!isset($_SESSION['user_id'])) {
$response['message'] = 'You are not logged in.';
echo json_encode($response);
exit();
}
$user_id = $_SESSION['user_id'];
// Check if form data is submitted
if (isset($_POST['current_password'], $_POST['new_password'], $_POST['confirm_password'])) {
$current_password = $_POST['current_password'];
$new_password = $_POST['new_password'];
$confirm_password = $_POST['confirm_password'];
// Validate new passwords
if ($new_password !== $confirm_password) {
$response['message'] = 'New passwords do not match.';
echo json_encode($response);
exit();
}
// Fetch the stored hashed password from the database
$sql = "SELECT password FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if (!$user) {
$response['message'] = 'User not found.';
echo json_encode($response);
exit();
}
// Verify the current password
if (!password_verify($current_password, $user['password'])) {
$response['message'] = 'Current password is incorrect.';
echo json_encode($response);
exit();
}
// Hash the new password
$new_password_hash = password_hash($new_password, PASSWORD_BCRYPT);
// Update the new password in the database
$sql = "UPDATE users SET password = ? WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("si", $new_password_hash, $user_id);
if ($stmt->execute()) {
$response['status'] = 'success';
$response['message'] = 'Password changed successfully.';
} else {
$response['message'] = 'Failed to change password.';
}
} else {
$response['message'] = 'Invalid form submission.';
}
echo json_encode($response);
?>

View File

@@ -0,0 +1,84 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php') ?>
<style>
@media (min-width: 991px) {
.container {
max-width: 720px;
padding: 0 15px; /* Ensure padding doesn't cause overflow */
}
</style>
<!-- Contact Form Area start -->
<section class="contact-form-area py-120 rel z-1">
<div class="container">
<div class="row align-items-center">
<!-- <div class="col-lg-6">
<div style="text-align: center;">
<img style="width:400px;" src="assets/images/logos/weblogo.png" alt="About">
</div>
</div> -->
<div class="">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="loginForm" class="loginForm" name="loginForm" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>Forgot your password?</h2>
<div class="pt-20" style="text-align: center;" id="responseMessage"></div> <!-- Message display area -->
</div>
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" class="form-control" placeholder="Enter Email" value="" required data-error="Please enter a valid Email address">
<div class="help-block with-errors"></div>
</div>
</div>
</div>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" class="theme-btn style-two" style="width:100%;">Send Link</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Form Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#loginForm').on('submit', function(event) {
event.preventDefault(); // Prevent the default form submission
$.ajax({
url: 'send_reset_link',
type: 'POST',
data: $(this).serialize(),
dataType: 'json',
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error sending link. Please contact support.</div>');
}
});
});
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

126
src/pages/auth/login.php Normal file
View File

@@ -0,0 +1,126 @@
<?php
$headerStyle = 'light';
// Determine the correct path to header.php based on file location
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// Include Google login PHP logic
require_once $rootPath . '/google-client/vendor/autoload.php';
$client = new Google_Client();
$client->setClientId('948441222188-8qhboq2urr8o9n35mc70s5h2nhd52v0m.apps.googleusercontent.com');
$client->setClientSecret('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');
$client->setRedirectUri($_ENV['HOST'] . '/validate_login.php');
$client->addScope("email");
$client->addScope("profile");
// 👇 Add this to force the account picker
$client->setPrompt('select_account');
$login_url = $client->createAuthUrl();
?>
<style>
@media (min-width: 991px) {
.container {
max-width: 720px;
padding: 0 15px; /* Ensure padding doesn't cause overflow */
}
}
</style>
<!-- Contact Form Area start -->
<section class="contact-form-area py-120 rel z-1">
<div class="container">
<div class="row align-items-center">
<!-- <div class="col-lg-6">
<div style="text-align: center;">
<img style="width:400px;" src="assets/images/logos/weblogo.png" alt="About">
</div>
</div> -->
<div class="">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="loginForm" class="loginForm" name="loginForm" action="validate_login" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>Log in</h2>
<div style="text-align: center;" id="responseMessage"></div> <!-- Message display area -->
</div>
<!-- Google login button -->
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<a href="<?php echo $login_url; ?>" style="width:100%;" class="theme-btn style-three">
<img src="/assets/images/google.png" alt="Google Icon" style="width:20px; height:20px; margin-right: 10px; vertical-align: middle;">
Login with Google
</a>
</div>
</div>
</div>
<div style="text-align: center; margin: 20px 0;">
<!-- <hr style="border: 1px solid #ddd; width: 50%; margin: 0 auto;"> -->
<span style="top: -10px; padding: 0 10px;">or</span>
</div>
<!-- Email login fields -->
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" class="form-control" placeholder="Enter Email" value="" required data-error="Please enter a valid Email address">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12"></div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Enter Password" value="" required data-error="Please enter your password">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<button type="submit" class="theme-btn style-two" style="width:100%;">Log In</button>
</div>
</div>
<div class="pt-20" style="text-align: center;">Don't have an account? <a href="register"><b>Register here.</b> </a>| <a href="forgot_password"><b>Forgot your password?</b></a></div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Form Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('#loginForm').on('submit', function(event) {
event.preventDefault(); // Prevent the default form submission
$.ajax({
url: '<?= url("validate_login") ?>',
type: 'POST',
data: $(this).serialize(),
dataType: 'json',
success: function(response) {
if (response.status === 'success') {
window.location.href = '<?= url("index") ?>';
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">An error occurred while processing your request.</div>');
}
});
});
});
</script>
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

174
src/pages/auth/register.php Normal file
View File

@@ -0,0 +1,174 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php') ?>
<style>
@media (min-width: 991px) {
.container {
max-width: 720px;
padding: 0 15px;
}
}
#passwordRequirements li {
transition: color 0.3s;
}
.text-success {
color: green !important;
}
.text-danger {
color: red !important;
}
</style>
<!-- Contact Form Area start -->
<section class="contact-form-area py-70 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="registerForm" name="registerForm" action="register_user" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>Register</h2>
</div>
<p>Register to create your 4WDCSA.co.za user profile.</p>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="first_name">First Name</label>
<input type="text" id="first_name" name="first_name" class="form-control" placeholder="John" value="" required data-error="Please enter your Name">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="last_name">Last Name</label>
<input type="text" id="last_name" name="last_name" class="form-control" placeholder="Smith" value="" required data-error="Please enter your Last Name">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="phone_number">Phone Number</label>
<input type="text" id="phone_number" name="phone_number" class="form-control" placeholder="Phone" value="" required data-error="Please enter your Phone">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" class="form-control" placeholder="Enter email" value="" required data-error="Please enter your Email">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Enter password" value="" required data-error="Please enter your password">
<ul id="passwordRequirements" class="small mt-2" style="list-style: none; padding-left: 0;">
<li id="length" class="text-danger">✗ At least 8 characters</li>
<li id="uppercase" class="text-danger">✗ At least one uppercase letter</li>
<li id="lowercase" class="text-danger">✗ At least one lowercase letter</li>
<li id="number" class="text-danger">✗ At least one number</li>
<li id="special" class="text-danger">✗ At least one special character</li>
</ul>
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="password_confirm">Confirm Password</label>
<input type="password" id="password_confirm" name="password_confirm" class="form-control" placeholder="Enter password" value="" required data-error="Please enter your password">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<button type="submit" class="theme-btn style-two" style="width:100%;">Register</button>
<div id="msgSubmit" class="hidden"></div>
</div>
<div id="responseMessage"></div> <!-- Message display area -->
</div>
<div class="pt-20">Already have an account? <a href="login"><b>Log in here.</b></a></div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Form Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
function updatePasswordFeedback(password) {
const length = password.length >= 8;
const uppercase = /[A-Z]/.test(password);
const lowercase = /[a-z]/.test(password);
const number = /[0-9]/.test(password);
const special = /[\W_]/.test(password);
$('#length').toggleClass('text-success', length).toggleClass('text-danger', !length).html(`${length ? '✓' : '✗'} At least 8 characters`);
$('#uppercase').toggleClass('text-success', uppercase).toggleClass('text-danger', !uppercase).html(`${uppercase ? '✓' : '✗'} At least one uppercase letter`);
$('#lowercase').toggleClass('text-success', lowercase).toggleClass('text-danger', !lowercase).html(`${lowercase ? '✓' : '✗'} At least one lowercase letter`);
$('#number').toggleClass('text-success', number).toggleClass('text-danger', !number).html(`${number ? '✓' : '✗'} At least one number`);
$('#special').toggleClass('text-success', special).toggleClass('text-danger', !special).html(`${special ? '✓' : '✗'} At least one special character`);
}
$('#password').on('input', function() {
const password = $(this).val();
updatePasswordFeedback(password);
});
$(document).ready(function() {
$('#registerForm').on('submit', function(event) {
event.preventDefault(); // Prevent default form submission
const password = $('#password').val();
const confirmPassword = $('#password_confirm').val();
const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/;
if (!passwordPattern.test(password)) {
$('#responseMessage').html(`
<div class="alert alert-danger">
Password must be at least 8 characters long, include uppercase and lowercase letters, a number, and a special character.
</div>
`);
return;
}
if (password !== confirmPassword) {
$('#responseMessage').html(`
<div class="alert alert-danger">Passwords do not match.</div>
`);
return;
}
// If validation passes, proceed with AJAX
$.ajax({
url: 'register_user',
type: 'POST',
data: $(this).serialize(),
dataType: 'json',
success: function(response) {
if (response.status === 'success') {
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
$('#registerForm')[0].reset(); // Optionally reset the form
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">An error occurred while processing your request.</div>');
}
});
});
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,41 @@
<?php
header('Content-Type: application/json');
$rootPath = dirname(dirname(dirname(__DIR__)));
require_once($rootPath . '/src/config/connection.php');
require_once($rootPath . '/src/config/functions.php');
require_once($rootPath . '/vendor/autoload.php');
use GuzzleHttp\Client;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data['email'], $data['name'], $data['token'])) {
echo json_encode([
'success' => false,
'message' => 'Missing required fields.'
]);
exit;
}
$email = $data['email'];
$name = $data['name'];
$token = $data['token'];
if (sendVerificationEmail($email, $name, $token)) {
$_SESSION['message'] = "Verification mail resend successful!";
echo json_encode([
'success' => true,
'message' => "Verification email sent to $email."
]);
} else {
$_SESSION['message'] = "Verification mail resend FAILED!";
echo json_encode([
'success' => false,
'message' => "Failed to send verification email to $email."
]);
}
}

View File

@@ -0,0 +1,113 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
$token = $_GET['token'] ?? '';
if (empty($token)) {
die("Invalid token.");
}
// Verify the token
$sql = "SELECT user_id FROM password_resets WHERE token = ? AND expires_at > NOW()";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $token);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
die("Token is invalid or expired.");
}
$user = $result->fetch_assoc();
$user_id = $user['user_id'];
// Display the reset password form
?>
<style>
@media (min-width: 991px) {
.container {
max-width: 720px;
padding: 0 15px; /* Ensure padding doesn't cause overflow */
}
</style>
<!-- Contact Form Area start -->
<section class="contact-form-area py-120 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="changePasswordForm" class="loginForm" name="changePasswordForm" action="update_password" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>Reset Password</h2>
<div class="pt-20" style="text-align: center;" id="responseMessage"></div> <!-- Message display area -->
</div>
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<label for="new_password">New Password</label>
<input type="password" id="new_password" name="new_password" class="form-control" placeholder="Enter password" value="" required data-error="Please enter your password">
<div class="help-block with-errors"></div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="confirm_password">Confirm Password</label>
<input type="password" id="confirm_password" name="confirm_password" class="form-control" placeholder="Confirm password" value="" required data-error="Please confirm your password">
<div class="help-block with-errors"></div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token); ?>">
<button type="submit" class="theme-btn style-two" style="width:100%;">Reset Password</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Form Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Change Password
$('#changePasswordForm').on('submit', function(event) {
event.preventDefault(); // Prevent default form submission
$.ajax({
url: 'update_password',
type: 'POST',
data: $(this).serialize(),
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage2').html('<div class="alert alert-danger">Error changing password.</div>');
}
});
});
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,61 @@
<?php
$rootPath = dirname(dirname(dirname(__DIR__)));
require_once($rootPath . '/src/config/env.php');
require_once($rootPath . '/src/config/connection.php');
require_once($rootPath . '/src/config/functions.php');
$response = array('status' => 'error', 'message' => 'Something went wrong');
if (isset($_POST['token'], $_POST['new_password'], $_POST['confirm_password'])) {
$token = $_POST['token'];
$new_password = $_POST['new_password'];
$confirm_password = $_POST['confirm_password'];
if ($new_password !== $confirm_password) {
$response['message'] = 'Passwords do not match.';
echo json_encode($response);
exit();
}
// Verify the token
$sql = "SELECT user_id FROM password_resets WHERE token = ? AND expires_at > NOW()";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $token);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 0) {
$response['message'] = 'Token is invalid or expired.';
echo json_encode($response);
exit();
}
$user = $result->fetch_assoc();
$user_id = $user['user_id'];
// Hash the new password
$new_password_hash = password_hash($new_password, PASSWORD_BCRYPT);
// Update the new password in the database
$sql = "UPDATE users SET password = ? WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("si", $new_password_hash, $user_id);
if ($stmt->execute()) {
// Delete the token from the database
$sql = "DELETE FROM password_resets WHERE token = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $token);
$stmt->execute();
$response['status'] = 'success';
$response['message'] = 'Password has been successfully reset.';
} else {
$response['message'] = 'Failed to reset password.';
}
} else {
$response['message'] = 'Invalid form submission.';
}
echo json_encode($response);
?>

39
src/pages/auth/verify.php Normal file
View File

@@ -0,0 +1,39 @@
<?php
$rootPath = dirname(dirname(dirname(__DIR__)));
require_once($rootPath . '/src/config/env.php');
require_once($rootPath . '/src/config/connection.php');
require_once($rootPath . '/src/config/functions.php');
// Create connection
$conn = openDatabaseConnection();
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Verify token
if (isset($_GET['token'])) {
$token = $conn->real_escape_string($_GET['token']);
// Prepare and execute query
$stmt = $conn->prepare('UPDATE users SET is_verified = 1 WHERE token = ?');
$stmt->bind_param('s', $token);
if ($stmt->execute()) {
if ($stmt->affected_rows > 0) {
header('Location: login.php');
} else {
header('Location: login.php');
}
} else {
echo 'Error: ' . $stmt->error;
}
$stmt->close();
} else {
echo 'No token provided.';
}
$conn->close();
?>

View File

@@ -0,0 +1,325 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession();
$user_id = $_SESSION['user_id'];
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
.message-box {
text-align: center;
position: relative;
padding: 10px;
padding-right: 35px;
/* Ensures text doesn't overlap with the close button */
}
.close-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
/* Centers vertically */
cursor: pointer;
font-size: 20px;
font-weight: bold;
color: #333;
background: none;
border: none;
}
.close-btn:hover {
color: red;
}
</style>
</style>
<?php
$pageTitle = 'My Bookings';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Tour List Area start -->
<section class="tour-list-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<?php if (isset($_SESSION['message'])): ?>
<div class="alert alert-warning message-box">
<?php echo $_SESSION['message']; ?>
<span class="close-btn" onclick="this.parentElement.style.display='none'">&times;</span>
</div>
<?php unset($_SESSION['message']); ?>
<?php endif; ?>
<div class="shop-shorter rel z-3 mb-20">
<!-- <ul class="grid-list mb-15 me-2">
<li><a href="#"><i class="fal fa-border-all"></i></a></li>
<li><a href="#"><i class="far fa-list"></i></a></li>
</ul> -->
<div class="sort-text mb-15 me-4 me-xl-auto">
<?php echo countUpcomingBookings($user_id); ?> Upcoming Bookings
</div>
<label>
<input type="checkbox" id="togglePastBookings" onchange="togglePastBookings()"> Show Past Bookings
</label>
<!-- <div class="sort-text mb-15 me-4">
Sort By
</div> -->
<!-- <select>
<option value="default" selected="">Sort By</option>
<option value="new">Newness</option>
<option value="old">Oldest</option>
<option value="hight-to-low">High To Low</option>
<option value="low-to-high">Low To High</option>
</select> -->
</div>
<?php
// Query to retrieve data from the bookings table
$sql = "SELECT * FROM bookings WHERE user_id = ? ORDER BY to_date DESC";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
// Loop through each row
while ($row = $result->fetch_assoc()) {
$booking_id = $row['booking_id'];
$booking_type = $row['booking_type'];
$from_date = $row['from_date'];
$to_date = $row['to_date'];
$num_vehicles = $row['num_vehicles'];
$num_adults = $row['num_adults'];
$num_children = $row['num_children'];
$add_firewood = $row['add_firewood'];
$total_amount = $row['total_amount'];
$discount_amount = $row['discount_amount'];
$status = $row['status'];
$trip_id = $row['trip_id'];
$course_id = $row['course_id'];
$course_nonmembers = $row['course_non_members'];
$radio = $row['radio'];
$amount = $total_amount - $discount_amount;
$total_adults = $num_adults + $course_nonmembers;
if (!is_null($trip_id)) {
// Prepare a SQL statement to retrieve trip details
$sql_trip = "SELECT trip_name, location, short_description, start_date, end_date FROM trips WHERE trip_id = ?";
$stmt_trip = $conn->prepare($sql_trip);
$stmt_trip->bind_param("i", $trip_id);
if ($stmt_trip->execute()) {
$result_trip = $stmt_trip->get_result();
if ($result_trip->num_rows > 0) {
// Fetch trip details
$trip_data = $result_trip->fetch_assoc();
$trip_name = $trip_data['trip_name'] ?? "Trip Name Placeholder";
$location = $trip_data['location'] ?? "Location Placeholder";
$short_description = $trip_data['short_description'] ?? "Short description of the trip.";
$start_date = $trip_data['start_date'] ?? $from_date; // Default to booking start date if not set
$end_date = $trip_data['end_date'] ?? $to_date; // Default to booking end date if not set
} else {
// Set default values if no trip data found
$trip_name = "Trip Name Placeholder";
$location = "Location Placeholder";
$short_description = "Short description of the trip.";
$start_date = $from_date; // Default to booking start date
$end_date = $to_date; // Default to booking end date
}
} else {
// Handle SQL execution error
echo "Error retrieving trip information: " . $stmt_trip->error;
}
// Close the statement
$stmt_trip->close();
} elseif (!is_null($course_id)) {
// Prepare a SQL statement to retrieve trip details
$sql_course = "SELECT course_type, date FROM courses WHERE course_id = ?";
$stmt_course = $conn->prepare($sql_course);
$stmt_course->bind_param("i", $course_id);
if ($stmt_course->execute()) {
$result_course = $stmt_course->get_result();
if ($result_course->num_rows > 0) {
// Fetch trip details
$trip_data = $result_course->fetch_assoc();
$date = $trip_data['date'] ?? "Location Placeholder";
$type = $trip_data['course_type'] ?? "Trip Name Placeholder";
if ($type === "driver_training") {
$trip_name = "Basic 4X4 Driver Training Course";
} elseif ($type === "bush_mechanics") {
$trip_name = "Bush Mechanics Course";
} elseif ($type === "rescue_recovery") {
$trip_name = "Rescue & Recovery Training Course";
} else {
$trip_name = "General Course"; // Default fallback description
}
$start_date = $date;
$end_date = $date;
$location = "BASE4, Hennops";
$short_description = getDetail($type);
} else {
// Set default values if no trip data found
$trip_name = "Trip Name Placeholder";
$location = "BASE4, Hennops";
$short_description = getDetail($type);
$start_date = $from_date; // Default to booking start date
$end_date = $to_date; // Default to booking end date
}
} else {
// Handle SQL execution error
echo "Error retrieving trip information: " . $stmt_course->error;
}
// Close the statement
$stmt_course->close();
} else {
// Set default values if trip_id is null
$trip_name = "BASE4 Camping";
$location = "BASE4, Hennops";
$short_description = "Please remember to bring 2 bags of firewood and drinking water for personal use.";
$start_date = $from_date; // Default to booking start date
$end_date = $to_date; // Default to booking end date
}
// Get today's date
$today = date("Y-m-d");
// Determine if the date is past or future
if ($end_date < $today) {
$tense = 'past';
} else {
$tense = 'future';
}
// Output the HTML structure with dynamic data
echo '
<div class="destination-item style-three bgc-lighter booking ' . $tense . '" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">';
if ($booking_type === 'trip') {
echo '<img src="assets/images/trips/' . $trip_id . '_01.jpg" alt="' . htmlspecialchars($trip_name) . '">';
} elseif ($booking_type === 'course') {
echo '<img src="assets/images/courses/' . $type . '.png" alt="' . htmlspecialchars($trip_name) . '">';
} else {
echo '<img style="width:450px;" src="assets/images/base4/base4.jpg" alt="Base4">';
}
echo '
</div>
<div class="content">
<div class="destination-header">
<span class="location"><i class="fal fa-map-marker-alt"></i> ' . htmlspecialchars($location) . '</span>
</div>
<h5>' . htmlspecialchars($trip_name) . '</a></h5>
<p>' . htmlspecialchars($short_description) . '</p>
<ul class="blog-meta">';
if ($booking_type === 'course') {
echo '<li><i class="far fa-calendar"></i> ' . convertDate($start_date) . '</li>';
} else {
echo '<li><i class="far fa-calendar"></i> ' . convertDate($start_date) . ' - ' . convertDate($end_date) . '</li>
<li><i class="far fa-clock"></i> ' . calculateDaysAndNights($start_date, $end_date) . '</li>';
} ?>
<li><i class="far fa-user"></i>
<?php
echo $num_vehicles . ' ' . ($num_vehicles > 1 ? 'vehicles' : 'vehicle') . ' ' .
$total_adults . ' ' . ($total_adults > 1 ? 'adults' : 'adult');
if ($num_children > 0) {
echo ' ' . $num_children . ' ' . ($num_children > 1 ? 'children' : 'child');
}
?>
</li>
<?php echo '
</ul>
<div class="destination-footer">
<span class="price"><span>Booking Total: R ' . number_format($amount, 2) . '</span></span>';
if ($status == "AWAITING PAYMENT") {
echo '<a href="' . url('payment_confirmation') . '?token=' . encryptData($booking_id, $salt) . '" class="theme-btn style-two style-three">
<span data-hover="PAYMENT INFO">' . $status . '</span>
</a>';
} else {
echo '<a href="" class="theme-btn style-two style-three">
<span data-hover="' . $status . '">' . $status . '</span>
</a>';
}
echo '
</div>
</div>
</div>';
}
} else {
echo '<p>You have no upcoming bookings.</p>';
}
// Close connection
$conn->close();
?>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<script>
function togglePastBookings() {
// Get the checkbox element
const checkbox = document.getElementById('togglePastBookings');
// Select all elements with the class 'past'
const pastBookings = document.querySelectorAll('.booking.past');
// Show or hide past bookings based on the checkbox state
pastBookings.forEach(booking => {
booking.style.display = checkbox.checked ? '' : 'none';
});
}
// Run the function on page load
document.addEventListener('DOMContentLoaded', () => {
// Set the initial state of the checkbox if needed
const checkbox = document.getElementById('togglePastBookings');
checkbox.checked = false; // Optional: Start with checkbox unchecked
// Call the function to set the initial state of past bookings
togglePastBookings();
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,217 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkUserSession();
?>
<!-- Gallery Area start -->
<section class="gallery-slider-area pt-100 rel z-1">
<div class="gallery-slider-active">
<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/gallery/gallery-slider1.jpg" alt="Gallery">
</div>
<div class="content">
<span class="category">Tour & Travel</span>
<h5><a href="destination-details.html">Brown Concrete Building</a></h5>
</div>
</div>
<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/gallery/gallery-slider2.jpg" alt="Gallery">
</div>
<div class="content">
<span class="category">Tour & Travel</span>
<h5><a href="destination-details.html">Brown Concrete Building</a></h5>
</div>
</div>
<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/gallery/gallery-slider3.jpg" alt="Gallery">
</div>
<div class="content">
<span class="category">Tour & Travel</span>
<h5><a href="destination-details.html">Brown Concrete Building</a></h5>
</div>
</div>
<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/gallery/gallery-slider4.jpg" alt="Gallery">
</div>
<div class="content">
<span class="category">Tour & Travel</span>
<h5><a href="destination-details.html">Brown Concrete Building</a></h5>
</div>
</div>
<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/gallery/gallery-slider5.jpg" alt="Gallery">
</div>
<div class="content">
<span class="category">Tour & Travel</span>
<h5><a href="destination-details.html">Brown Concrete Building</a></h5>
</div>
</div>
</div>
</section>
<!-- Gallery Area end -->
<!-- About Us Area start -->
<section class="about-us-area pt-90 pb-100 rel z-1">
<div class="container">
<div class="row gap-100 align-items-center">
<div class="col-lg-6">
<div class="destination-details-content rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title mb-25">
<span class="h2 mb-15">Welcome to </span>
<h2>BASE4 Camping</h2>
</div>
<p>Escape to the ultimate outdoor adventure at BASE4, nestled right next to a tranquil stream. Enjoy the perfect blend of rugged exploration and relaxation with top-notch facilities, including braai areas, hot showers, and clean ablution blocks. Gather with friends under our spacious lapa or take a dip in the refreshing swimming pool after a day of off-road fun. Whether you're conquering trails or kicking back by the fire, our campsite offers the ideal setting for an unforgettable getaway. Book your spot today and experience nature at its finest!</p>
</div>
<div class="widget widget-booking" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Book your Campsite</h5>
<form action="process_camp_booking" method="POST">
<div class="date mb-25">
<b>From Date</b>
<input type="date" id="from_date" name="from_date">
</div>
<div class="date mb-25">
<b>To Date</b>
<input type="date" id="to_date" name="to_date">
</div>
<hr class="mb-25">
<ul class="tickets clearfix">
<li>
No. of Vehicles
<input style="border-color: #d7d7d7;width:auto;padding:10px;" type="number" id="vehicles" name="vehicles" value="1" min="1">
</li>
<li>
No. of Adults
<input style="border-color: #d7d7d7;width:auto;padding:10px;" type="number" id="adults" name="adults" value="1" min="1">
</li>
<li>
No. of Children
<input style="border-color: #d7d7d7;width:auto;padding:10px;" type="number" id="children" name="children" value="0" min="0">
</li>
</ul>
<hr class="mb-25">
<h6>Add Extra:</h6>
<ul class="radio-filter pt-5">
<li>
<input class="form-check-input" type="checkbox" name="AddExtra" id="add-extra1" value="50">
<label for="add-extra1">2 x 5kg Firewood <span>R 50,00</span></label>
</li>
</ul>
<hr>
<?php if ($is_member) : ?>
<div id="discount_section">
<h6>Discount:</h6>
<ul class="radio-filter pt-5">
<li>
<label for="add-extra1">4WDCSA Member Discount <span id="discount_amount">R 0,00</span></label>
</li>
</ul>
<hr>
</div>
<?php endif ?>
<h6>Total: <span id="booking_total" class="price">-</span></h6>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5">
<span data-hover="Book Now">Book Now</span>
<i class="fal fa-arrow-right"></i>
</button>
<div class="text-center">
<a href="contact.html">Need some help?</a> | Payments will be redirected to Payfast.
</div>
</form>
</div>
</div>
<div class="col-lg-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="destination-map">
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d667.578212275918!2d28.000752737032542!3d-25.864032288240537!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x1e95794b858a5427%3A0xcdb0a4b0055a9753!2sFour%20Wheel%20Drive%20Club%20of%20Southern%20Africa%20-FWDCSA%20GAUTENG%20-%20BASE%204!5e1!3m2!1sen!2sza!4v1726669599601!5m2!1sen!2sza" width="100%" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</div>
</div>
</div>
</section>
<!-- About Us Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Function to calculate booking total
function calculateTotal() {
var fromDate = new Date($('#from_date').val());
var toDate = new Date($('#to_date').val());
var vehicles = parseInt($('#vehicles').val()) || 1;
var firewoodCost = $('#add-extra1').is(':checked') ? 50 : 0;
var isMember = <?php echo $is_member ? 'true' : 'false'; ?>;
var perNightRate = 200;
if (isMember) {
perNightRate = 0; // 100% discount
}
// Calculate nights
var timeDifference = toDate.getTime() - fromDate.getTime();
var nights = Math.ceil(timeDifference / (1000 * 3600 * 24));
if (nights < 1) {
nights = 0; // If "To Date" is before "From Date", no charge
}
// Calculate total
var total = (nights * perNightRate * vehicles) + firewoodCost;
// Update total in the DOM
$('#booking_total').text('R ' + total.toFixed(2));
// Update discount section
if (isMember) {
var discountAmount = nights * 200 * vehicles; // Original rate * nights * vehicles
$('#discount_amount').text('R ' + discountAmount.toFixed(2));
}
}
// Function to restrict date selection
function restrictDates() {
var today = new Date().toISOString().split('T')[0]; // Get today's date in YYYY-MM-DD format
$('#from_date').attr('min', today); // Set min for from_date
var fromDate = $('#from_date').val();
if (fromDate) {
$('#to_date').attr('min', fromDate); // Set min for to_date based on from_date
} else {
$('#to_date').attr('min', today); // Default to today's date if no from_date is set
}
}
// Event listeners to trigger recalculation and date restriction
$('#from_date').on('change', function() {
restrictDates();
calculateTotal();
});
$('#to_date, #vehicles, #add-extra1').on('change', function() {
calculateTotal();
});
// Initial setup for date restrictions and calculation
restrictDates();
calculateTotal();
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,195 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
$conn = openDatabaseConnection();
$stmt = $conn->prepare("SELECT * FROM campsites");
$stmt->execute();
$result = $stmt->get_result();
$campsites = [];
while ($row = $result->fetch_assoc()) {
$campsites[] = $row;
}
?>
<style>
#map {
height: 600px;
width: 100%;
}
.gm-style .info-box {
max-width: 250px;
}
.info-box img {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
</style>
<?php
$pageTitle = 'Campsites';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Tour List Area start -->
<section class="tour-list-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div id="map" style="width: 100%; height: 500px;"></div>
<!-- Add Campsite Modal -->
</div>
</div>
</div>
</section>
<div class="modal fade" id="addCampsiteModal" tabindex="-1">
<div class="modal-dialog">
<form id="addCampsiteForm" method="POST" action="add_campsite" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add Campsite</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" name="latitude" id="latitude">
<input type="hidden" name="longitude" id="longitude">
<div class="mb-3">
<label class="form-label">Campsite Name</label>
<input type="text" class="form-control" name="name" required>
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control" name="description" rows="3"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Booking URL</label>
<input type="url" class="form-control" name="website">
</div>
<div class="mb-3">
<label class="form-label">Phone Number</label>
<input type="text" class="form-control" name="telephone">
</div>
<div class="mb-3">
<label class="form-label">Thumbnail Image</label>
<input type="file" class="form-control" name="thumbnail" accept="image/*">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="submit">Save Campsite</button>
<button class="btn btn-secondary" type="button" data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</form>
</div>
</div>
<script>
let map;
const campsites = <?php echo json_encode($campsites); ?>;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
center: {
lat: -28.0,
lng: 24.0
}, // SA center
zoom: 6,
});
map.addListener("click", function(e) {
const lat = e.latLng.lat();
const lng = e.latLng.lng();
document.getElementById("latitude").value = lat;
document.getElementById("longitude").value = lng;
const addModal = new bootstrap.Modal(document.getElementById("addCampsiteModal"));
addModal.show();
});
// Load existing campsites from PHP
fetch("get_campsites.php")
.then(response => response.json())
.then(data => {
data.forEach(site => {
const marker = new google.maps.Marker({
position: {
lat: parseFloat(site.latitude),
lng: parseFloat(site.longitude)
},
map,
title: site.name,
});
const content = `
<div class="info-box">
<strong>${site.name}</strong><br>
${site.description ? site.description + "<br>" : ""}
${site.website ? `<a href="${site.website}" target="_blank">Visit Website</a><br>` : ""}
${site.telephone ? `Phone: ${site.telephone}<br>` : ""}
${site.thumbnail ? `<img src="${site.thumbnail}" style="width: 100%; max-width: 200px; border-radius: 8px; margin-top: 5px;">` : ""}
${site.user && site.user.first_name ? `
<div class="user-info mt-2 d-flex align-items-center">
<img src="${site.user.profile_pic}" style="width: 40px; height: 40px; border-radius: 50%; object-fit: cover; margin-right: 10px;">
<div>
<small>Added by:</small><br>
<strong>${site.user.first_name} ${site.user.last_name}</strong>
</div>
</div>` : ""}
<br>
<button class="btn btn-sm btn-warning mt-2" onclick='editCampsite(${JSON.stringify(site)})'>Edit</button>
<a href="https://www.google.com/maps/dir/?api=1&destination=${site.latitude},${site.longitude}" target="_blank" class="btn btn-sm btn-outline-primary mt-2 ms-2">Get Directions</a>
</div>
`;
const infowindow = new google.maps.InfoWindow({
content: content
});
marker.addListener("click", () => {
infowindow.open(map, marker);
});
});
})
.catch(err => console.error("Failed to load campsites:", err));
}
function editCampsite(site) {
// Pre-fill form
document.querySelector("#addCampsiteForm input[name='name']").value = site.name;
document.querySelector("#addCampsiteForm textarea[name='description']").value = site.description || "";
document.querySelector("#addCampsiteForm input[name='website']").value = site.website || "";
document.querySelector("#addCampsiteForm input[name='telephone']").value = site.telephone || "";
document.querySelector("#addCampsiteForm input[name='latitude']").value = site.latitude;
document.querySelector("#addCampsiteForm input[name='longitude']").value = site.longitude;
// Add hidden ID input
let idInput = document.querySelector("#addCampsiteForm input[name='id']");
if (!idInput) {
idInput = document.createElement("input");
idInput.type = "hidden";
idInput.name = "id";
document.querySelector("#addCampsiteForm").appendChild(idInput);
}
idInput.value = site.id;
// Show the modal
const addModal = new bootstrap.Modal(document.getElementById("addCampsiteModal"));
addModal.show();
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyC-JuvnbUYc8WGjQBFFVZtKiv5_bFJoWLU&callback=initMap" async defer></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,303 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// SQL query to fetch dates for driver training
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ?");
$course_type = 'driver_training';
$stmt->bind_param("s", $course_type);
$stmt->execute();
$result = $stmt->get_result();
?>
<?php
$pageTitle = 'Course Details';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Page Banner End -->
<!-- Product Details Start -->
<section class="product-details pt-100">
<div class="container">
<div class="row">
<div class="col-lg-6">
<div class="product-details-images rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-content preview-images">
<div class="tab-pane fade preview-item active show" id="preview1">
<img src="assets/images/shop/preview1.png" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview2">
<img src="assets/images/shop/preview1.png" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview3">
<img src="assets/images/shop/preview1.png" alt="Perview">
</div>
</div>
<div class="nav thumb-images rmb-20">
<a href="#preview1" data-bs-toggle="tab" class="thumb-item active show">
<img src="assets/images/shop/thumb1.png" alt="Thumb">
</a>
<a href="#preview2" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/shop/thumb2.png" alt="Thumb">
</a>
<a href="#preview3" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/shop/thumb3.png" alt="Thumb">
</a>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="product-details-content" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>4X4 Driver Training</h2>
</div>
<!-- <div class="ratting mb-15">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
<span>(5.8k+ reviews)</span>
</div> -->
<span class="price mb-5">R 50,00/member</span>
<span class="price mb-25">R 750,00/non-members</span>
<p>Our 4x4 Basic Training Course equips you with the essential skills and knowledge to confidently tackle off-road terrains. Learn vehicle mechanics, driving techniques, obstacle navigation, and recovery methods while promoting safe and responsible off-road practices. Perfect for beginners and new 4x4 owners!</p>
<hr class="mt-40">
<form action="#" class="add-to-cart pt-15 pb-30">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<label for="course_date">Select a Date:</label>
<select name="course_date" id="course_date" required>
<!-- <option value="" disabled selected>-- Select a Date --</option> -->
<?php
if ($result->num_rows > 0) {
// Output each course as an option
while ($row = $result->fetch_assoc()) {
$course_id = htmlspecialchars($row['course_id']); // Escape output for security
$date = htmlspecialchars($row['date']); // Escape output for security
echo "<option value='$course_id'>$date</option>";
}
} else {
echo "<option value='' disabled>No dates available</option>";
}
?>
</select>
<button type="submit" class="theme-btn style-two bgc-secondary">
<span data-hover="Add to Cart">Book Now</span>
<i class="far fa-arrow-right"></i>
</button>
</form>
<hr class="mb-45">
<a href="#" class="wishlist"><i class="far fa-heart"></i> Add to Wishlist</a>
</div>
</div>
</div>
<ul class="nav nav product-tab mt-70 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li><a href="#details" data-bs-toggle="tab" class="active show">Course Overview<i class="far fa-arrow-right"></i></a></li>
<li><a href="#information" data-bs-toggle="tab">What to Expect<i class="far fa-arrow-right"></i></a></li>
<!-- <li><a href="#reviews" data-bs-toggle="tab"> Reviews <i class="far fa-arrow-right"></i></a></li> -->
</ul>
<div class="tab-content" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-pane fade active show" id="details">
<p>A 4x4 Basic Training Course is designed to equip participants with the foundational knowledge and practical skills necessary for safe and effective off-road driving. This course covers essential topics such as understanding the mechanics of 4x4 vehicles, selecting the appropriate gear, and engaging various drive modes to tackle different terrains. Participants will learn how to navigate obstacles like mud, sand, and rocky paths while maintaining vehicle control and ensuring safety for themselves and their passengers. The training also includes instruction on tire pressure management, vehicle recovery techniques, and the use of essential recovery equipment like tow straps and shackles.</p>
<p>In addition to practical driving exercises, the course emphasizes responsible off-road driving practices, including respecting the environment and adhering to trail etiquette. Whether you're a novice driver looking to explore off-road adventures or a new 4x4 owner seeking confidence behind the wheel, this training provides a comprehensive introduction to the world of off-roading. By the end of the course, participants will feel prepared to tackle basic off-road challenges with skill and assurance, making their next 4x4 outing a safe and enjoyable experience.</p>
<div class="row gap-50 pt-25 pb-20 align-items-center">
<div class="col-lg-7 pt-15">
<h5>What this course includes</h5>
<ul class="list-style-two mt-25">
<li>Basic Driver Training Manual.</li>
<li>Theory session and discussion.</li>
<li>Spend the afternoon on the track learning the basic practices of 4X4 driving.</li>
</ul>
</div>
<div class="col-lg-5">
<div class="image rmt-45">
<img src="assets/images/shop/product-details.jpg" alt="Product Details">
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="information">
<!-- <p>Circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses</p> -->
<ul class="list-style-two my-35">
<li>Coffee and Welcome: Start the day with a warm cup of coffee, meet the instructors, and get an overview of the training schedule.</li>
<li>Theory Session: Learn the fundamentals of 4x4 vehicle mechanics, terrain types, recovery equipment, and off-road safety.</li>
<li>Practical Demonstrations: Observe demonstrations of essential techniques like gear selection, tire pressure adjustment, and recovery setups.</li>
<li>Lunch Break: Bring along a packed lunch or something to braai. Fires will be provided.</li>
<li>Track Driving and Practical Training: Put theory into action with hands-on driving exercises on a custom-designed off-road track.</li>
<li>Debrief and Certificates: Wrap up the day with a recap of key lessons, feedback from instructors, and certificates of completion.</li>
</ul>
</div>
<!-- <div class="tab-pane fade mb-20" id="reviews">
<h5>2 Reviews</h5>
<div class="comments my-30">
<div class="comment-body" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="author-thumb">
<img src="assets/images/blog/comment-author1.jpg" alt="Author">
</div>
<div class="content">
<h6>Lonnie B. Horwitz</h6>
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<span class="time">Venice, Rome and Milan 9 Days 8 Nights</span>
<p>Tours and travels play a crucial role in enriching lives by offering unique experiences, cultural exchanges, and the joy of exploration.</p>
<a class="read-more" href="#">Reply <i class="far fa-angle-right"></i></a>
</div>
</div>
<div class="comment-body" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="author-thumb">
<img src="assets/images/blog/comment-author3.jpg" alt="Author">
</div>
<div class="content">
<h6>Jaime B. Wilson</h6>
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<span class="time">Venice, Rome and Milan 9 Days 8 Nights</span>
<p>Tours and travels play a crucial role in enriching lives by offering unique experiences, cultural exchanges, and the joy of exploration.</p>
<a class="read-more" href="#">Reply <i class="far fa-angle-right"></i></a>
</div>
</div>
</div> -->
<!-- <h5>Add A Reviews</h5>
<form id="comment-form" class="comment-form bgc-lighter z-1 rel mt-30" name="review-form" action="#" method="post" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="row gap-20">
<div class="col-md-6">
<div class="form-group">
<input type="text" id="full-name" name="full-name" class="form-control" placeholder="Name" value="" required="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="email" id="email-address" name="email" class="form-control" placeholder="Email" value="" required="">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<textarea name="message" id="message" class="form-control" rows="5" placeholder="Comments" required=""></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" class="theme-btn bgc-secondary style-two">
<span data-hover="Submit reviews">Submit review</span>
<i class="fal fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</form> -->
<!-- </div> -->
</div>
</div>
</section>
<!-- Product Details End -->
<!-- Shop Details Area start -->
<section class="shop-details-page pt-80 pb-100 rel z-1">
<div class="container">
<div class="section-title text-center mb-40">
<h2>Other Courses</h2>
</div>
<div class="product-slider">
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/shop/product1.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Airport Travel Suitcases</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="50">
<div class="image">
<img src="assets/images/shop/product2.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Travel Great blue hat</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="100">
<div class="image">
<img src="assets/images/shop/product3.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Waistband and Mesh Fashion</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="150">
<div class="image">
<img src="assets/images/shop/product4.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Sandals for Casual Techies</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/shop/product5.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Children With Jute Soles</a></h6>
<span class="price">$188.00</span>
</div>
</div>
</div>
</div>
</section>
<!-- Shop Details Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,389 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession();
// SQL query to fetch dates for driver training
$stmt = $conn->prepare("SELECT course_id, date
FROM courses
WHERE course_type = ?
AND date >= CURDATE()");
$course_type = 'driver_training';
$stmt->bind_param("s", $course_type);
$stmt->execute();
$result = $stmt->get_result();
$page_id = 'driver_training';
?>
<style>
.form-group {
margin-bottom: 15px;
}
select {
width: 100%;
padding: 8px;
font-size: 16px;
}
</style><?php
$pageTitle = 'Driver Training';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Product Details Start -->
<section class="product-details pt-100">
<div class="container">
<div class="row">
<div class="col-lg-6">
<div class="product-details-images rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-content preview-images">
<div class="tab-pane fade preview-item active show" id="preview1">
<img src="assets/images/drivertraining/01.png" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview2">
<img src="assets/images/drivertraining/07.jpg" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview3">
<img src="assets/images/drivertraining/02.png" alt="Perview">
</div>
</div>
<div class="nav thumb-images rmb-20">
<a href="#preview1" data-bs-toggle="tab" class="thumb-item active show">
<img src="assets/images/drivertraining/01.png" alt="Thumb">
</a>
<a href="#preview2" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/drivertraining/07.jpg" alt="Thumb">
</a>
<a href="#preview3" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/drivertraining/02.png" alt="Thumb">
</a>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="product-details-content" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>4X4 Driver Training</h2>
</div>
<!-- <div class="ratting mb-15">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
<span>(5.8k+ reviews)</span>
</div> -->
<span class="price mb-5">R <?= getPrice('driver_training', 'member'); ?>/member</span>
<span class="price mb-25">R <?= getPrice('driver_training', 'nonmember'); ?>/non-members</span>
<p>Our 4x4 Basic Training Course equips you with the essential skills and knowledge to confidently tackle off-road terrains. Learn vehicle mechanics, driving techniques, obstacle navigation, and recovery methods while promoting safe and responsible off-road practices. Perfect for beginners and new 4x4 owners!</p>
<hr class="mt-40">
<div class="blog-sidebar tour-sidebar">
<div class="widget widget-booking" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="process_course_booking" method="POST">
<ul class="tickets clearfix">
<li>
Select Date
<select name="course_id" id="course_id" required>
<?php
if ($result && $result->num_rows > 0) {
// Output each course as an option
while ($row = $result->fetch_assoc()) {
$course_id = htmlspecialchars($row['course_id']); // Escape output for security
$date = htmlspecialchars($row['date']); // Escape output for security
echo "<option value='$course_id'>$date</option>";
}
} else {
echo "<option value='' disabled>No dates available</option>";
}
?>
</select>
</li>
<?php
if ($is_member || $pending_member) {
echo '
<li>
Additional Members <span class="price"></span>
<select name="members" id="members">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
';
} ?>
<li>
Additional Non-Members <span class="price"></span>
<select name="non-members" id="non-members">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
</ul>
<hr class="mb-25">
<h6>Total: <span id="booking_total" class="price">-</span></h6>
<div style="margin: 20px 0;">
<div id="indemnityBox" style="border: 1px solid #ccc; padding: 10px; height: 150px; overflow-y: scroll; background: #f9f9f9; font-size: 12px;">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div style="margin-top: 10px;">
<input type="checkbox" id="agreeCheckbox" name="agree" disabled required>
<label for="agreeCheckbox" id="agreeLabel" style="color: #888;">I have read and agree to the indemnity terms</label>
</div>
</div>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<?php
$button_text = "Book Now";
$button_disabled = "";
if (!$result || $result->num_rows == 0) {
$button_text = "No booking dates available";
$button_disabled = "disabled";
}
?>
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5" <?php echo $button_disabled; ?>>
<span data-hover="<?php echo $button_text; ?>"><?php echo $button_text; ?></span>
<i class="fal fa-arrow-right"></i>
</button>
<div class="text-center">
<a href="contact">Need some help?</a>
</div>
</form>
</div>
</div>
<!-- <hr class="mb-45"> -->
<!-- <a href="#" class="wishlist"><i class="far fa-heart"></i> Add to Wishlist</a> -->
</div>
</div>
</div>
<ul class="nav nav product-tab mt-70 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li><a href="#details" data-bs-toggle="tab" class="active show">Course Overview<i class="far fa-arrow-right"></i></a></li>
<li><a href="#information" data-bs-toggle="tab">What to Expect<i class="far fa-arrow-right"></i></a></li>
<li><a href="#reviews" data-bs-toggle="tab"> Reviews <i class="far fa-arrow-right"></i></a></li>
</ul>
<div class="tab-content" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-pane fade active show" id="details">
<p>A 4x4 Basic Training Course is designed to equip participants with the foundational knowledge and practical skills necessary for safe and effective off-road driving. This course covers essential topics such as understanding the mechanics of 4x4 vehicles, selecting the appropriate gear, and engaging various drive modes to tackle different terrains. Participants will learn how to navigate obstacles like mud, sand, and rocky paths while maintaining vehicle control and ensuring safety for themselves and their passengers. The training also includes instruction on tire pressure management, vehicle recovery techniques, and the use of essential recovery equipment like tow straps and shackles.</p>
<p>In addition to practical driving exercises, the course emphasizes responsible off-road driving practices, including respecting the environment and adhering to trail etiquette. Whether you're a novice driver looking to explore off-road adventures or a new 4x4 owner seeking confidence behind the wheel, this training provides a comprehensive introduction to the world of off-roading. By the end of the course, participants will feel prepared to tackle basic off-road challenges with skill and assurance, making their next 4x4 outing a safe and enjoyable experience.</p>
<div class="row gap-50 pt-25 pb-20 align-items-center">
<div class="col-lg-7 pt-15">
<h5>What this course includes</h5>
<ul class="list-style-two mt-25">
<li>Basic Driver Training Manual.</li>
<li>Theory session and discussion.</li>
<li>Spend the afternoon on the track learning the basic practices of 4X4 driving.</li>
</ul>
</div>
<div class="col-lg-5">
<div class="image rmt-45">
<img src="assets/images/drivertraining/dt04.jpg" alt="Product Details">
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="information">
<!-- <p>Circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses</p> -->
<ul class="list-style-two my-35">
<li>Coffee and Welcome: Start the day with a warm cup of coffee, meet the instructors, and get an overview of the training schedule.</li>
<li>Theory Session: Learn the fundamentals of 4x4 vehicle mechanics, terrain types, recovery equipment, and off-road safety.</li>
<li>Practical Demonstrations: Observe demonstrations of essential techniques like gear selection, tire pressure adjustment, and recovery setups.</li>
<li>Lunch Break: Bring along a packed lunch or something to braai. Fires will be provided.</li>
<li>Track Driving and Practical Training: Put theory into action with hands-on driving exercises on a custom-designed off-road track.</li>
<li>Debrief and Certificates: Wrap up the day with a recap of key lessons, feedback from instructors, and certificates of completion.</li>
</ul>
</div>
<div class="tab-pane fade mb-20" id="reviews">
<?php include_once('review_box.php'); ?>
</div>
</div>
</div>
</section>
<!-- Product Details End -->
<!-- Shop Details Area start -->
<!-- <section class="shop-details-page pt-80 pb-100 rel z-1">
<div class="container">
<div class="section-title text-center mb-40">
<h2>Other Courses</h2>
</div>
<div class="product-slider">
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/shop/product1.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Airport Travel Suitcases</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="50">
<div class="image">
<img src="assets/images/shop/product2.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Travel Great blue hat</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="100">
<div class="image">
<img src="assets/images/shop/product3.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Waistband and Mesh Fashion</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="150">
<div class="image">
<img src="assets/images/shop/product4.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Sandals for Casual Techies</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/shop/product5.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Children With Jute Soles</a></h6>
<span class="price">$188.00</span>
</div>
</div>
</div>
</div>
</section> -->
<!-- Shop Details Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
const indemnityBox = document.getElementById('indemnityBox');
const agreeCheckbox = document.getElementById('agreeCheckbox');
const bookingForm = document.querySelector('form');
indemnityBox.addEventListener('scroll', function() {
const scrollTop = indemnityBox.scrollTop;
const scrollHeight = indemnityBox.scrollHeight;
const offsetHeight = indemnityBox.offsetHeight;
// Enable checkbox when scrolled to bottom
if (scrollTop + offsetHeight >= scrollHeight - 1) {
agreeCheckbox.disabled = false;
document.getElementById('agreeLabel').style.color = "#000"; // optional: make label active
}
});
bookingForm.addEventListener('submit', function(e) {
if (agreeCheckbox.disabled || !agreeCheckbox.checked) {
alert('Please read and agree to the indemnity terms before booking.');
e.preventDefault(); // stop form submission
}
});
</script>
<script>
$(document).ready(function() {
// Function to calculate booking total
function calculateTotal() {
// Get selected values from the form
var members = parseInt($('#members').val()) || 0; // Default to 1 vehicle if not selected
var nonmembers = parseInt($('#non-members').val()) || 0; // Default to 1 adult if not selected
// Fetch PHP variables
var isMember = <?php echo $is_member ? 'true' : 'false'; ?>;
var pendingMember = <?php echo $pending_member ? 'true' : 'false'; ?>;
var cost_members = <?= getPrice('driver_training', 'member'); ?>;
var cost_nonmembers = <?= getPrice('driver_training', 'nonmember'); ?>;
// Calculate the total cost based on membership
var total = 0;
// Calculate cost for members
if (isMember || pendingMember) {
total = (cost_members) + (members * cost_members) + (nonmembers * cost_nonmembers);
} else {
// Calculate cost for non-members
total = (cost_nonmembers) + (members * cost_members) + (nonmembers * cost_nonmembers);
}
// Update total price in the DOM
$('#booking_total').text('R ' + total.toFixed(2));
}
// Event listeners to trigger recalculation when any form field changes
$('#members, #non-members').on('change', function() {
calculateTotal();
});
// Initial calculation on page load
calculateTotal();
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,676 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkUserSession();
if (!isset($_GET['token']) || empty($_GET['token'])) {
die("Invalid request.");
}
$token = $_GET['token'];
// echo $token;
// Sanitize the trip_id to prevent SQL injection
$trip_id = intval(decryptData($token, $salt)); // Ensures $trip_id is treated as an integer
// Prepare the SQL query
$sql = "SELECT trip_id, trip_name, location, short_description, long_description, start_date, end_date,
vehicle_capacity, cost_members, cost_nonmembers, places_booked, booking_fee, cost_pensioner, cost_pensioner_member
FROM trips
WHERE trip_id = ?";
// Use prepared statements for added security
$stmt = $conn->prepare($sql);
if ($stmt) {
// Bind the parameter
$stmt->bind_param("i", $trip_id);
// Execute the query
$stmt->execute();
// Get the result
$result = $stmt->get_result();
// Check if the trip exists
if ($result->num_rows > 0) {
// Fetch the data
$row = $result->fetch_assoc();
// Populate the variables
$trip_id = $row['trip_id'];
$trip_name = $row['trip_name'];
$location = $row['location'];
$short_description = $row['short_description'];
$long_description = $row['long_description'];
$start_date = $row['start_date'];
$end_date = $row['end_date'];
$capacity = $row['vehicle_capacity'];
$cost_members = $row['cost_members'];
$cost_nonmembers = $row['cost_nonmembers'];
$cost_pensioner = $row['cost_pensioner'];
$cost_pensioner_member = $row['cost_pensioner_member'];
$member_discount = $cost_nonmembers - $cost_members;
$member_discount_pensioner = $cost_pensioner - $cost_pensioner_member;
$places_booked = $row['places_booked'];
$booking_fee = $row['booking_fee'];
$remaining_places = getAvailableSpaces($trip_id);
// Determine the badge text based on the status
$badge_text = ($remaining_places > 0) ? $remaining_places . ' PLACES LEFT!!' : 'FULLY BOOKED';
// Convert newlines into <br> tags to preserve line breaks
$formatted_description = nl2br($long_description);
// Wrap the text in <p> tags at the beginning and end
$formatted_description = '<p>' . $formatted_description . '</p>';
} else {
echo "No trip found with ID: $trip_id";
}
// Close the statement
$stmt->close();
} else {
echo "Error preparing the statement: " . $conn->error;
}
// Always close the database connection when done
$conn->close();
?>
<style>
.gallery-item {
position: relative;
overflow: hidden;
width: 100%;
padding-top: 66.66%;
border-radius: 10px;
/* This is equivalent to a 3:2 aspect ratio (height/width * 100) */
}
.gallery-item img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.gallery-item-portrait {
position: relative;
overflow: hidden;
width: 100%;
padding-top: 136%;
/* This is equivalent to a 3:2 aspect ratio (height/width * 100) */
}
.allery-item-portrait img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.price-display {
display: block;
/* Ensures each price display is on a new line */
margin-bottom: 8px;
/* Adds space between the two price blocks */
}
.price-wrapper {
display: inline-flex;
align-items: baseline;
/* Align the price and '/per person' on the same baseline */
}
.price {
font-size: 3rem;
font-weight: bold;
margin: 0;
vertical-align: baseline;
}
.per-person {
font-size: 1rem;
margin-left: 8px;
color: #777;
/* Optional: makes '/per person' text softer */
vertical-align: baseline;
}
.non-member .price {
font-size: 1.5rem;
/* Smaller price for non-members */
color: rgb(86, 86, 86);
/* Optional: makes non-member price stand out */
}
</style>
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
</style>
<section class=" pt-50 pb-35 rel z-1 ">
<div class="container">
<div class="banner-inner text-black mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50"><?php echo $trip_name; ?></h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index">Home</a></li>
<li class="breadcrumb-item active">4WDCSA Trips</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Tour Gallery start -->
<div class="tour-gallery">
<div class="container-fluid">
<div class="row gap-10 justify-content-center rel">
<div class="col-lg-4 col-md-6">
<div class="gallery-item image-landscape">
<img src="assets/images/trips/<?php echo $trip_id; ?>_01.jpg" alt="">
</div>
<div class="gallery-item image-landscape">
<img src="assets/images/trips/<?php echo $trip_id; ?>_02.jpg" alt="">
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="gallery-item gallery-item-portrait image-portrait">
<img src="assets/images/trips/<?php echo $trip_id; ?>_03.jpg" alt="">
</div>
</div>
<div class="col-lg-4 col-md-6">
<div class="gallery-item image-landscape">
<img src="assets/images/trips/<?php echo $trip_id; ?>_04.jpg" alt="">
</div>
<div class="gallery-item image-landscape">
<img src="assets/images/trips/<?php echo $trip_id; ?>_05.jpg" alt="">
</div>
</div>
<!-- <div class="col-lg-12">
<div class="gallery-more-btn">
<a href="contact.html" class="theme-btn style-two bgc-secondary">
<span data-hover="See All Photos">See All Photos</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div> -->
</div>
</div>
</div>
<!-- Tour Gallery End -->
<!-- Tour Header Area start -->
<section class="tour-header-area pt-70 rel z-1">
<div class="container">
<div class="row justify-content-between">
<div class="col-xl-6 col-lg-7">
<div class="tour-header-content mb-15" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<span class="location d-inline-block mb-10"><i class="fal fa-map-marker-alt"></i> <?php echo $location; ?></span>
<div class="section-title pb-5">
<h2><?php echo $trip_name; ?></h2>
</div>
<!-- <div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div> -->
</div>
<span class="subtitle mb-15"><?php echo $badge_text; ?></span>
</div>
<!-- <div class="col-xl-4 col-lg-5 text-lg-end" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="tour-header-social mb-10">
<a href="#"><i class="far fa-share-alt"></i>Share tours</a>
<a href="#"><i class="fas fa-heart bgc-secondary"></i>Wish list</a>
</div>
</div> -->
</div>
<hr class="mt-50 mb-70">
</div>
</section>
<!-- Tour Header Area end -->
<!-- Tour Details Area start -->
<section class="tour-details-page pb-100">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="tour-details-content">
<h3>Trip Info</h3>
<p><?php echo $formatted_description; ?></p>
<div class="price-display">
<div class="price-wrapper">
<h2 class="price">R <?php echo $cost_members; ?></h2><span class="per-person">/per member</span>
</div>
</div>
<div class="price-display non-member">
<div class="price-wrapper">
<h2 class="price">R <?php echo $cost_nonmembers; ?></h2><span class="per-person">/per non-member</span>
</div>
</div>
<br>
<div class="price-display non-member">
<div class="price-wrapper">
<h2 class="price">R <?php echo $booking_fee; ?></h2><span class="per-person">/club fee per vehicle</span>
</div>
</div>
<!-- <div class="row pb-55">
<div class="col-md-6">
<div class="tour-include-exclude mt-30">
<h5>Included and Excluded</h5>
<ul class="list-style-one check mt-25">
<li><i class="far fa-check"></i> Pick and Drop Services</li>
<li><i class="far fa-check"></i> 1 Meal Per Day</li>
<li><i class="far fa-check"></i> Cruise Dinner & Music Event</li>
<li><i class="far fa-check"></i> Visit 7 Best Places in the City</li>
<li><i class="far fa-check"></i> Bottled Water on Buses</li>
<li><i class="far fa-check"></i> Transportation Luxury Tour Bus</li>
</ul>
</div>
</div>
<div class="col-md-6">
<div class="tour-include-exclude mt-30">
<h5>Excluded</h5>
<ul class="list-style-one mt-25">
<li><i class="far fa-times"></i> Gratuities</li>
<li><i class="far fa-times"></i> Hotel pickup and drop-off</li>
<li><i class="far fa-times"></i> Lunch, Food & Drinks</li>
<li><i class="far fa-times"></i> Optional upgrade to a glass</li>
<li><i class="far fa-times"></i> Additional Services</li>
<li><i class="far fa-times"></i> Insurance</li>
</ul>
</div>
</div>
</div> -->
</div>
<!-- <h3>Activities</h3>
<div class="tour-activities mt-30 mb-45">
<div class="tour-activity-item">
<i class="flaticon-hiking"></i>
<b>Hiking</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-fishing"></i>
<b>Fishing</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-man"></i>
<b>Kayak shooting</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-kayak-1"></i>
<b>Kayak</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-bonfire"></i>
<b>Campfire</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-flashlight"></i>
<b>Night Exploring</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-cycling"></i>
<b>Biking</b>
</div>
<div class="tour-activity-item">
<i class="flaticon-meditation"></i>
<b>Yoga</b>
</div>
</div> -->
<!-- <h3>Itinerary</h3>
<div class="accordion-two mt-25 mb-60" id="faq-accordion-two">
<div class="accordion-item">
<h5 class="accordion-header">
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#collapseTwoOne">
Day 1 - Arrive at campground
</button>
</h5>
<div id="collapseTwoOne" class="accordion-collapse collapse" data-bs-parent="#faq-accordion-two">
<div class="accordion-body">
<p>To take a trivial example which undertakes laborious physical exercise except to obtain some advantage pleasure annoying consequences</p>
</div>
</div>
</div>
<div class="accordion-item">
<h5 class="accordion-header">
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#collapseTwoTwo">
Day 2 - Wake up early and embark on a day hike
</button>
</h5>
<div id="collapseTwoTwo" class="accordion-collapse collapse" data-bs-parent="#faq-accordion-two">
<div class="accordion-body">
<p>The early start ensures you can fully immerse yourself in the tranquility of nature before the world fully awakens. As the morning light filters through the trees, you'll experience the crisp, fresh air and the peaceful sounds of the forest. The trail ahead offers both a physical challenge promise of breathtaking.</p>
</div>
</div>
</div>
<div class="accordion-item">
<h5 class="accordion-header">
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#collapseTwoThree">
Day 3 - Join a guided ranger-led nature walk
</button>
</h5>
<div id="collapseTwoThree" class="accordion-collapse collapse" data-bs-parent="#faq-accordion-two">
<div class="accordion-body">
<p>To take a trivial example which undertakes laborious physical exercise except to obtain some advantage pleasure annoying consequences</p>
</div>
</div>
</div>
<div class="accordion-item">
<h5 class="accordion-header">
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#collapseTwoFour">
Day 4 - Take a break from hiking
</button>
</h5>
<div id="collapseTwoFour" class="accordion-collapse collapse" data-bs-parent="#faq-accordion-two">
<div class="accordion-body">
<p>To take a trivial example which undertakes laborious physical exercise except to obtain some advantage pleasure annoying consequences</p>
</div>
</div>
</div>
<div class="accordion-item">
<h5 class="accordion-header">
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#collapseTwoFive">
Day 5 - Pack a lunch and embark on a longer hike
</button>
</h5>
<div id="collapseTwoFive" class="accordion-collapse collapse" data-bs-parent="#faq-accordion-two">
<div class="accordion-body">
<p>To take a trivial example which undertakes laborious physical exercise except to obtain some advantage pleasure annoying consequences</p>
</div>
</div>
</div>
</div> -->
<!-- <h3>Maps</h3> -->
<div class="tour-map mt-30 mb-50">
<iframe src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d13894.816708766162!2d29.256367272652284!3d-29.46664742147583!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x1ef37aefd73de6bd%3A0xf35ffec07e766685!2sDrakensberg!5e0!3m2!1sen!2sza!4v1750666087092!5m2!1sen!2sza" style="border:0; width: 100%;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</div>
<div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar tour-sidebar">
<div class="widget widget-booking" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Book your Trip</h5>
<form action="process_trip_booking" method="POST">
<input type="hidden" name="trip_id" id="trip_id" value="<?php echo $trip_id; ?>">
<ul class="radio-filter pt-5">
<li>
<label for="add-extra1">Start Date <span><?php echo $start_date; ?></span></label>
</li>
</ul>
<ul class="radio-filter pt-5">
<li>
<label for="add-extra1">End Date <span><?php echo $end_date; ?></span></label>
</li>
</ul>
<hr class="mb-25">
<ul class="tickets clearfix">
<li>
Vehicles <span class="price"></span>
<select name="vehicles" id="vehicles">
<option value="1" selected>01</option>
<option value="2">02</option>
<option value="3">03</option>
<option value="4">04</option>
</select>
</li>
<li>
Adults <span class="price"></span>
<select name="adults" id="adults">
<option value="0">00</option>
<option value="1" selected>01</option>
<option value="2">02</option>
<option value="3">03</option>
<option value="4">04</option>
</select>
</li>
<li>
Children <span class="price"></span>
<select name="children" id="children">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
<li>
Pensioners <span class="price"></span>
<select name="pensioners" id="pensioners">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
</ul>
<!-- <hr class="mb-25"> -->
<!-- <h6>Extras:</h6> -->
<!-- <ul class="radio-filter pt-5">
<li>
<input class="form-check-input" type="checkbox" name="AddExtra" id="add-extra1" value="50" style="background:#fff;">
<label for="add-extra1">4WDCSA Pensioner Discount </label>
</li>
</ul> -->
<hr>
<?php if ($is_member) : ?>
<div id="discount_section">
<h6>Discount:</h6>
<ul class="radio-filter pt-5">
<li>
<label for="add-extra1">4WDCSA Member Discount <span id="discount_amount">R 0,00</span></label>
</li>
</ul>
<hr>
</div>
<?php endif ?>
<ul class="radio-filter pt-5">
<li>
<label for="add-extra1">4WDCSA Booking Fee <span id="booking_fee">R <?php echo $booking_fee; ?></span></label>
</li>
</ul>
<div style="margin: 20px 0;">
<div id="indemnityBox" style="border: 1px solid #ccc; padding: 10px; height: 150px; overflow-y: scroll; background: #f9f9f9; font-size: 12px;">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div style="margin-top: 10px;">
<input type="checkbox" id="agreeCheckbox" name="agree" disabled required>
<label for="agreeCheckbox" id="agreeLabel" style="color: #888;">I have read and agree to the indemnity terms</label>
</div>
</div>
<h6>Total: <span id="booking_total" class="price">-</span></h6>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<?php if ($remaining_places < 1): ?>
<button type="button" class="theme-btn style-two w-100 mt-15 mb-5" disabled>
<span>FULLY BOOKED</span>
<i class="fal fa-times-circle"></i>
</button>
<?php else: ?>
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5">
<span data-hover="Book Now">Book Now</span>
<i class="fal fa-arrow-right"></i>
</button>
<?php endif; ?>
<div class="text-center">
<a href="contact">Need some help?</a>
</div>
</form>
</div>
<div class="widget widget-contact" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Need Help?</h5>
<ul class="list-style-one">
<li><i class="far fa-envelope"></i> <a href="mailto:info@4wdcsa.co.za">info@4wdcsa.co.za</a></li>
<li><i class="far fa-phone-volume"></i> <a href="#">+27 79 065 2795</a></li>
</ul>
</div>
<!-- <div class="widget widget-cta" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="content text-white">
<span class="h6">Explore The World</span>
<h3>Best Tourist Place</h3>
<a href="tour-grid.html" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Now">Explore Now</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
<div class="image">
<img src="assets/images/widgets/cta-widget.png" alt="CTA">
</div>
<div class="cta-shape"><img src="assets/images/widgets/cta-shape3.png" alt="Shape"></div>
</div> -->
</div>
</div>
</div>
</div>
</section>
<!-- Tour Details Area end -->
<!-- About Us Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<!-- Shop Details Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
const indemnityBox = document.getElementById('indemnityBox');
const agreeCheckbox = document.getElementById('agreeCheckbox');
const bookingForm = document.querySelector('form');
indemnityBox.addEventListener('scroll', function() {
const scrollTop = indemnityBox.scrollTop;
const scrollHeight = indemnityBox.scrollHeight;
const offsetHeight = indemnityBox.offsetHeight;
// Enable checkbox when scrolled to bottom
if (scrollTop + offsetHeight >= scrollHeight - 1) {
agreeCheckbox.disabled = false;
document.getElementById('agreeLabel').style.color = "#000"; // optional: make label active
}
});
bookingForm.addEventListener('submit', function(e) {
if (agreeCheckbox.disabled || !agreeCheckbox.checked) {
alert('Please read and agree to the indemnity terms before booking.');
e.preventDefault(); // stop form submission
}
});
</script>
<script>
$(document).ready(function() {
// Function to calculate booking total
function calculateTotal() {
// Get selected values from the form
var vehicles = parseInt($('#vehicles').val()) || 1; // Default to 1 vehicle if not selected
var adults = parseInt($('#adults').val()) || 0; // Default to 1 adult if not selected
var pensioners = parseInt($('#pensioners').val()) || 0; // Default to 1 adult if not selected
var children = parseInt($('#children').val()) || 0; // Default to 0 children if not selected
var radio = $('#add-extra1').is(':checked') ? 50 : 0; // Extra cost for radio rental
// Fetch PHP variables
const isMember = <?php echo isset($is_member) && $is_member ? 'true' : 'false'; ?>;
const cost_members = <?php echo $cost_members ?? 0; ?>;
const cost_nonmembers = <?php echo $cost_nonmembers ?? 0; ?>;
const cost_pensioner = <?php echo $cost_pensioner ?? 0; ?>;
const cost_pensioner_member = <?php echo $cost_pensioner_member ?? 0; ?>;
const member_discount = <?php echo $member_discount ?? 0; ?>;
const member_discount_pensioner = <?php echo $member_discount_pensioner ?? 0; ?>;
const booking_fee = <?php echo $booking_fee ?? 0; ?>;
// Calculate the total cost based on membership
let total = 0;
let discountAmount = 0;
if (isMember) {
total = ((adults + children) * cost_members) + (pensioners * cost_pensioner_member) + radio + (vehicles * booking_fee);
discountAmount = ((adults + children) * member_discount) + (pensioners * member_discount_pensioner);
} else {
total = ((adults + children) * cost_nonmembers) + (pensioners * cost_pensioner) + radio + (vehicles * booking_fee);
}
$('#booking_total').text('R ' + total.toFixed(2));
if (isMember) {
$('#discount_amount').text('R ' + discountAmount.toFixed(2));
$('#discount_section').show();
} else {
$('#discount_section').hide();
}
}
$('#vehicles, #adults, #children, #pensioners, #add-extra1').on('change', calculateTotal);
calculateTotal();
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,141 @@
<?php
$headerStyle = 'light';
// Determine the correct path to header.php based on file location
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
</style>
<?php
$pageTitle = 'Trips';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Tour List Area start -->
<section class="tour-list-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="shop-shorter rel z-3 mb-20">
<!-- <ul class="grid-list mb-15 me-2">
<li><a href="#"><i class="fal fa-border-all"></i></a></li>
<li><a href="#"><i class="far fa-list"></i></a></li>
</ul> -->
<div class="sort-text mb-15 me-4 me-xl-auto">
<?php echo getTripCount();?> Trips available
</div>
<!-- <div class="sort-text mb-15 me-4">
Sort By
</div>
<select>
<option value="default" selected="">Sort By</option>
<option value="new">Newness</option>
<option value="old">Oldest</option>
<option value="hight-to-low">High To Low</option>
<option value="low-to-high">Low To High</option>
</select> -->
</div>
<?php
// Query to retrieve data from the trips table
$sql = "SELECT trip_id, trip_name, location, short_description, start_date, end_date, vehicle_capacity, cost_members, places_booked FROM trips WHERE published = 1 AND start_date > CURDATE()";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// Loop through each row
while ($row = $result->fetch_assoc()) {
$trip_id = $row['trip_id'];
$trip_name = $row['trip_name'];
$location = $row['location'];
$short_description = $row['short_description'];
$start_date = $row['start_date'];
$end_date = $row['end_date'];
$capacity = $row['vehicle_capacity'];
$cost_members = $row['cost_members'];
$places_booked = $row['places_booked'];
$remaining_places = getAvailableSpaces($trip_id);
// Determine the badge text based on the status
$badge_text = ($remaining_places > 0) ? $remaining_places.' PLACES LEFT!!' : 'FULLY BOOKED';
// Output the HTML structure with dynamic data
echo '
<div class="destination-item style-three bgc-lighter" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<span class="badge bgc-pink">' . $badge_text . '</span>
<img src="assets/images/trips/' . $trip_id . '_01.jpg" alt="' . $trip_name . '">
</div>
<div class="content">
<div class="destination-header">
<span class="location"><i class="fal fa-map-marker-alt"></i> ' . $location . '</span>
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
</div>
</div>
<h5><a href="trip-details.php?token=' . encryptData($trip_id, $salt) . '">' . $trip_name . '</a></h5>
<p>' . $short_description . '</p>
<ul class="blog-meta">
<li><i class="far fa-calendar"></i> ' . convertDate($start_date) . ' - ' . convertDate($end_date) . '</li>
<li><i class="far fa-clock"></i> '.calculateDaysAndNights($start_date, $end_date).'</li>
<li><i class="far fa-user"></i>' . $capacity . ' vehicles max</li>
</ul>
<div class="destination-footer">
<span class="price"><span>R ' . $cost_members . '</span>/person</span>
<a href="trip-details.php?token=' . encryptData($trip_id, $salt) . '" class="theme-btn style-two style-three">
<span data-hover="Book Now">Book Now</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
</div>';
}
}
// Close connection
$conn->close();
?>
<!-- <ul class="pagination pt-15 flex-wrap" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li class="page-item disabled">
<span class="page-link"><i class="far fa-chevron-left"></i></span>
</li>
<li class="page-item active">
<span class="page-link">
1
<span class="sr-only">(current)</span>
</span>
</li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">...</a></li>
<li class="page-item">
<a class="page-link" href="#"><i class="far fa-chevron-right"></i></a>
</li>
</ul> -->
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,258 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
$page_id = 'agm_minutes';
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
</style>
<style>
body {
/* font-family: Arial, sans-serif; */
line-height: 1.6;
/* max-width: 800px; */
margin: auto;
/* padding: 20px; */
}
h1,
h2 {
color: #2c3e50;
}
h2 {
margin-top: 2em;
}
.content {
margin-bottom: 2em;
}
.img-left,
.img-right {
max-width: 30%;
margin: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
.img-left {
float: left;
}
.img-right {
float: right;
}
</style>
<?php
$pageTitle = '2025 AGM Minutes';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Blog Detaisl Area start -->
<section class="blog-detaisl-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="blog-details-content" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<a href="blog.html" class="category">Report</a>
<ul class="blog-meta mb-30">
<li><img src="assets/images/pp/default.png" alt="Admin"> <a href="#">John Runciman</a></li>
<li><i class="far fa-calendar-alt"></i> <a href="#">5 April 2025</a></li>
<li><i class="far fa-comments"></i> <a href="#">Comments (<?= getCommentCount($page_id);?>)</a></li>
</ul>
<h2>2025 AGM Minutes & Chairman's Report</h2>
<?php include_once('agm_content.php');?>
</div>
<hr class="mb-45">
<div class="tag-share mb-50">
<div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<h6>Tags </h6>
<div class="tag-coulds">
<a href="blog">Reports</a>
</div>
</div>
<!-- <div class="item" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<h6>Share </h6>
<div class="social-style-one">
<a href="#"><i class="fab fa-facebook-f"></i></a>
<a href="#"><i class="fab fa-twitter"></i></a>
<a href="#"><i class="fab fa-linkedin-in"></i></a>
<a href="#"><i class="fab fa-instagram"></i></a>
</div>
</div> -->
</div>
<!-- <div class="admin-comment bgc-lighter" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="comment-body">
<div class="author-thumb">
<img src="assets/images/blog/admin-comment.jpg" alt="Author">
</div>
<div class="content">
<h4>Richard M. Fudge</h4>
<p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p>
<div class="social-icons">
<a href="contact.html"><i class="fab fa-facebook-f"></i></a>
<a href="contact.html"><i class="fab fa-twitter"></i></a>
<a href="contact.html"><i class="fab fa-linkedin-in"></i></a>
<a href="contact.html"><i class="fab fa-instagram"></i></a>
</div>
</div>
</div>
</div> -->
<!-- <form id="comment-form" class="comment-form bgc-lighter z-1 rel mt-25" name="review-form" action="#" method="post" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Leave A Comment</h5>
<p>Your email address will not be published. Required fields are marked *</p>
<div class="row gap-20 mt-30">
<div class="col-md-6">
<div class="form-group">
<input type="text" id="full-name" name="full-name" class="form-control" placeholder="Name" value="" required="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="email" id="email-address" name="email" class="form-control" placeholder="Email" value="" required="">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<textarea name="message" id="message" class="form-control" rows="5" placeholder="Message" required=""></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<ul class="radio-filter mb-25">
<li>
<input class="form-check-input" type="radio" name="terms-condition" id="terms-condition">
<label for="terms-condition">Save my name, email, and website in this browser for the next time I comment.</label>
</li>
</ul>
<button type="submit" class="theme-btn style-two">
<span data-hover="Send Comments">Send Comments</span>
<i class="fal fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</form> -->
<?php include_once('comment_box.php'); ?>
</div>
<div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar">
<!-- <div class="widget widget-search" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="#" class="default-search-form">
<input type="text" placeholder="Search" required="">
<button type="submit" class="searchbutton far fa-search"></button>
</form>
</div> -->
<!-- <div class="widget widget-category" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Category</h5>
<ul class="list-style-three">
<li><a href="blog.html">Adventure</a></li>
<li><a href="blog.html">Hiking & Trekking</a></li>
<li><a href="blog.html">Cycling Tours</a></li>
<li><a href="blog.html">Family Tours</a></li>
<li><a href="blog.html">Mountain Hiking</a></li>
<li><a href="blog.html">Rafting Excursion</a></li>
<li><a href="blog.html">Coastal Paragliding</a></li>
</ul>
</div> -->
<!-- <div class="widget widget-news" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Recent News</h5>
<ul>
<li>
<div class="image">
<img src="assets/images/widgets/news1.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Unique Destinations an tolded Stories ways</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news2.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Immersive Experiences from Around Globe</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news3.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Journey to Inspire Your Next Adventure</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
</ul>
</div> -->
<div class="widget widget-gallery" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Gallery</h5>
<div class="gallery">
<?php
$folder = 'assets/images/blog/2/';
$files = glob($folder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
shuffle($files); // Randomize the order
foreach ($files as $file) {
echo '<a href="' . $file . '" style="width: 110px; height: 110px; overflow: hidden; display: inline-block; margin: 2px;">';
echo '<img src="' . $file . '" alt="Gallery" style="width: 100%; height: 100%; object-fit: cover; display: block;">';
echo '</a>';
}
?>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Blog Detaisl Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,384 @@
<style>
.dropcap {
float: left;
font-size: 3em;
line-height: 1;
padding-right: 0.1em;
font-weight: bold;
}
</style>
<p><strong>DATE:</strong> 05 April 2025 at 10h00<br>
<strong>VENUE:</strong> Base 4
</p>
<p>
<h6><strong>NOTICE CONVENING THE MEETING</strong></h6>
<ul>
<li>Proposed: Peter Hutchison</li>
<li>Seconded: Doug Timm</li>
<li>Attendance register will be available on request</li>
</ul>
</p>
<p>
<h6><strong>WELCOME, APOLOGIES AND PROXIES</strong></h6>
<ul>
<li>Present: 30</li>
<li>Proxies: 15 Total: 45</li>
<li>Apologies: 2</li>
<li>Quorum confirmed</li>
</ul>
</p>
<p>
<h6><strong>ACCEPTANCE OF THE AGENDA WITH ANY ADDITIONAL ITEMS FROM THE FLOOR</strong></h6>
<ul>
<li>Proposed: Roy Olivier</li>
<li>Seconded: Davin Webster</li>
</ul>
</p>
<p>
<h6><strong>CONFIRMATION OF THE MINUTES OF THE PREVIOUS AGM OF 25 MARCH 2023</strong></h6>
<ul>
<li>Matters arising from the previous minutes: None</li>
<li>Proposed: Dave Nixon</li>
<li>Seconded: Peter Hutchison</li>
</ul>
</p>
<h3><strong>CHAIRMANS REPORT</strong></h3>
<p><span class="dropcap">I</span> am honoured to be standing up here today to welcome you all to the 2025 AGM! We have a lot to cover so I wont drag this out.
It makes me think of my father when he gave a talk at school or at scouts where he went on and on, and we said he did not need a watch, he needed a calendar!</p>
<h6><strong>FIRE</strong></h6>
<p class="content">
<img style="max-width: 45%;" src="assets/images/blog/2/agm.jpg" alt="Base 4 Fire" class="img-left">
<p>The biggest event of last year, or in fact the biggest event in the history of the Club, was the devastating fire that swept through Base 4. It occurred on a very windy day when a veldfire swept through the area burning everything in its path including I believe 6 homesteads. At Base 4 the Clubhouse and all the contents burned to the ground. If you look at the hulk over there you can only imagine the heat and destruction! The lapa down in the camping area suffered the same loss, leaving smouldering thatch which took days to cool.</p>
<p>The only things left standing were the vehicle service structure and the container, the wooden ablution block down in the camping area, and the brick and mortar ablution block on the Northern side of Base 4.
Whereto from there? We are not an outdoor adventure club for nothing! Everyone got stuck in and assisted in clearing the rubble, reconnecting the water and restoring electricity to the pool. A huge thank you to all those that put their backs to the wheel!</p>
<p>Luckily, the vehicle service structure was still standing, so with a few adjustments and additions, we held the next open day there, a great success! I believe the turnout exceeded the standard turnout in the old Clubhouse.</p>
</p>
<h6><strong>SPECIAL GENERAL MEETING</strong></h6>
<p>Following the fire we had to negotiate the insurance claim. With good planning all the requirements as stipulated by the insurers were up to date and current, including the thatch upgrade earlier in the year and the recent issue of the Electrical Certificate of Compliance amongst other things ensured that there were no serious issues with the insurance value, and with the help of Geoff Joubert, the valuation of the insurance payout was submitted to the Club in record breaking time.</p>
<p>This valuation gave us an option: either get the Clubhouse rebuilt to its former glory or take the cash which amounted to about 70% of the rebuild tender (the Indemnity Value). Choosing one or the other was not a decision that could be made by the Committee, hence the calling of a Special General Meeting so that the decision could be made by the Membership.</p>
<p>There were really 2 decisions that needed to be made at that SGM: keep Base 4 or sell it, and when the decision was made to keep Base 4, whether to get the Clubhouse rebuilt or to take the money. The decision was to take the money and to reinvent the Club more in line with the current membership numbers and needs.</p>
<p>All Members were then invited to submit proposals for the future of the Club to be considered and voted for at this Annual General Meeting. I will deal with these proposals later as dictated by the Agenda.</p>
<h6><strong>BASE 4 MAINTENANCE</strong></h6>
<p>Base 4 is a big piece of ground and needs continuous and on-going maintenance. As mentioned earlier, the fire destroyed much of the infrastructure especially water pipes and electrical cabling. Thank you to the generous members that donated time, money and product ensuring that the basic services were in place to allow Base 4 to operate.</p>
<p>The grass still needs cutting, the tracks maintained for driver training and for members to hone their skills. The ablution blocks require on-going upkeep and cleaning, fences repaired, water pumped and the myriad of jobs that need to be done but no one thinks about.</p>
<h6><strong>CLUB SECRETARY</strong></h6>
<p>Karl Hoffmans name is synonymous with Base 4 and the Four Wheel Drive Club. He has been the go-to person for the Club for many years! This last year Karl stepped down from the position as Club Secretary due to ill-health: more about that later.</p>
<p>The Committee has appointed Jacqui Boshoff to the position of Secretary, and we welcome her with open arms; I ask you all support her going forward.</p>
<p>Please make a note regarding the Club contact details:
<ul>
<li>info@4wdcsa.co.za</li>
<li>4wdcsa@gmail.com</li>
<li>079 065 2795</li>
</ul>
</p>
<h6><strong>THANK YOUS</strong></h6>
<p>I want to thank all those that have put in time and effort this past year</p>
<p><strong>The Committee</strong>
<ul>
<li>Peter Hutchison</li>
<li>Chris Pinto</li>
<li>Doug Timm</li>
<li>Noelene Runciman</li>
<li>Dorota Maskowicz</li>
<li>Noel Thompson</li>
<li>Dave Nixon</li>
</ul>
</p>
<p><strong>The Breakfast brigade</strong>
<ul>
<li>Linda Hutchison</li>
<li>Clara Hitge</li>
<li>Lesley Joubert</li>
<li>Louise Blignaut</li>
<li>Carol Corlett</li>
<li>Stan Salida</li>
<li>Ashley Salida</li>
</ul>
</p>
<p><strong>Event organisation</strong>
<ul>
<li>Noelene Runciman</li>
<li>Dorota Maskowicz</li>
</ul>
</p>
<p><strong>Base 4 maintenance</strong>
<ul>
<li>Dave Nixon</li>
<li>Peter Hutchison</li>
<li>Dave Bell</li>
<li>Andre Botha</li>
<li>Andrew Maier</li>
<li>Davin Webster</li>
<li>Clive Murray</li>
<li>Doug Galloway</li>
<li>Jenny Crickmore-Thompson</li>
<li>John Franklin</li>
<li>Marion Nichols</li>
<li>Richard Carter</li>
<li>Chas Dean</li>
<li>Rudolf Engelmann</li>
<li>Nelson Larangeira</li>
<li><strong>Base 4</strong>: Kingsley Mankhusu</li>
</ul>
</p>
<p>If I have left anyone out, please forgive me!</p>
<p>
<h6><strong>OUTINGS</strong></h6>
<p>This has been a bumper year for outings, 11 in total.
<ul>
<li>Best of the Eastern Cape (February 2024)</li>
<li>Parys Dome Extended Trip (March 2024)</li>
<li>Piesangskloof Day Trip (May 2024)</li>
<li>Botsalano Game Reserve extended trip (June)</li>
<li>Groenkloof Day Trip (June 2024)</li>
<li>Hobby Park Krugersdorp (August). Sadly no one turned up</li>
<li>Old Mill Drift Extended Trip (September)</li>
<li>Rust de Winter Weekend Trip (October 2024)</li>
<li>Northern Natal Bush and Beach Extended Trip (November 2024)</li>
<li>Mabibi Turtle Hatching Extended Trip (February 2025)</li>
<li>Marakele National Park Extended Trip (March 2025)</li>
</ul>
</p>
<p>
<h6><strong>OPEN DAYS</strong></h6>
<ul>
<li>February 2024 Chris Dykes on his 650km walk through the Kruger Park</li>
<li>March 2024 Carol Corlett on Coffee</li>
<li>April 2024 Anita Musevenzo on Save the Bees</li>
<li>May 2024 4x4 Poker Rally</li>
<li>June 2024 Any pot will do cook-off</li>
<li>July 2024 Rob Milne on Anecdotes of the Boer War</li>
<li>August 2024 Bob Boden on Leopards of the Magaliesburg</li>
<li>September 2024 Spring clean</li>
<li>October 2024 Kevin Davie on Rock Art</li>
<li>November 2925 Tarryn Johnston on Hennops Revival</li>
<li>February 2025 Jack Kapp on Trip Report to Botswana and Zimbabwe</li>
<li>March 2025 Wayne van Onselen on Unchain our Children</li>
</ul>
</p>
<p>
<h6><strong>EVENTS</strong></h6>
<ul>
<li>Annual General Meeting (April)</li>
<li>4x4 Poker Trail Fun Rally (May)</li>
<li>Any pot will do cook-off (June) Winner Sandy Nixon</li>
<li>Potjie cooking competition (July) Winner Muzzy</li>
<li>Special General Meeting (November 2024)</li>
<li>Christmas Party (December)</li>
</ul>
</p>
<p>
<h6><strong>DRIVER TRAINING</strong></h6>
<p>Another great perk of Club Membership is free driver training, make use of it!</p>
<ul>
<li>Driver Training Course (March 2024)</li>
<li>Bush Mechanics Course (April 2024)</li>
<li>Rescue and Recovery Course (June 2024)</li>
<li>Driver Training Course (July 2024)</li>
<li>Bush Mechanics Course (October 2024)</li>
<li>Ladies Driver Training (March 2025)</li>
<li>Driver training course (March 2025)</li>
</ul>
</p>
<h3><strong>ADDRESS</strong></h3>
<p>
<span class="dropcap">A</span> little later this morning we are going to make some decisions about the future of Base 4.
What I want to talk about is the future of The Four Wheel Club of Southern Africa, Gauteng region.
</p>
<p>
The burning down of our Clubhouse and Lapa can be seen as a blessing. This gives us the opportunity of starting afresh:
new ideas, fresh thoughts. Historically we have spent our years worrying about money and funds and costs.
For now, we do not have that hanging over our heads and I want to promote fun, and outings, and camping,
and all the good things we associate with being a member of an Outdoor Adventure Club.
</p>
<p>
The upswing in outings and events this last year show that we are moving in that direction!
</p>
<h6>Membership</h6>
<p>For members to get the full benefit of membership requires participation.</p>
<p><strong>Camping.</strong> We offer free camping; come and enjoy parking off under the trees and listen to the gurgling of the stream that is flowing so strongly at the moment. Anyone wanting to camp is not restricted to open weekends, Base 4 is open to you any day or days of the month; it just needs a bit of notice to organise the water and opening the gate etc.</p>
<p><strong>Driver training.</strong> Free to members. We run three different training subjects: basic driver training, rescue and recovery, and bush mechanics courses. We recently ran a very successful Ladies Driver Training and will be offering follow-up days.</p>
<p><strong>Open Days and Open Weekends.</strong> Committee members try very hard to get interesting guest speakers and events to make those days and weekends fun and exciting. Come and join in. Bring the family and friends for a picnic on the Sunday and relax under the trees or around the pool.</p>
<p><strong>Outings.</strong> As members you get preferential rates: day trips, weekend trips and extended trips. The Club does not make a vast profit on these outings, just enough to cover costs. Get out there and experience other places, other trails, other like-minded people.</p>
<p>
These benefits only start there! Getting or giving advice from knowledgeable people.
Sharing adventures, enjoying new travel companions.
As I said, getting the benefit of your membership requires your participation.
</p>
<h6>Running the Club</h6>
<p>
We have 5 Members on the Committee at this AGM, and those 5 are suffering overload because they are shouldering all the work.
We are a voluntary organisation, and the Committee Members are starting to resent the fact that they are expected to carry on
regardless and rewardless. These Committee Members are there for the same reason you are there for, fun, education and excitement.
If no assistance and change of attitude comes to the fore, there will be no Committee next year and the Club will drown!
</p>
<p>
Historically Committee Members had portfolios that they managed, Outings, Events, Estate management, Driver training and so on.
The idea was that these portfolios would be managed by those Members but what happened in reality is that the Member ended up having
to do all the work themselves. For example, Noelene found and organised 6 of the speakers at open days last year.
I organised 7 and led 5 of the outings last year, and I ran 5 of the 7 Driver Training courses. As I said, we cannot go on like this!
</p>
<p>
Going forward, the Committee (or the Management Team) is mandated to run the Club along acceptable company practices,
including the financial administration, record keeping, advertising and marketing. Further, the Team will assist in coordinating
and organising outings, events etc. but the responsibility for organising and running of these portfolios will now lie with the Membership.
</p>
<p>
Amongst yourselves volunteer or delegate Members to lead trips, find speakers, organise events. Organise workdays at Base 4.
Out of the membership of 80 (current paid up) people this load can be shared by having one person doing only one task a year.
Only one! Come on! We need to share the load.
</p>
<p>
I will stick to my side of the bargain. I will organise and lead at least 1 extended outing this year,
and I will conduct driver training for the rest of the year.
</p>
<p><strong>Please get involved!</strong></p>
<h3><strong>GERALD OBRIEN</strong></h3>
<p>I have pleasure in announcing that the Committee after due consideration has decided to bestow Honorary Life Membership on Gerald OBrien.</p>
<p>Gerald has been a loyal member of our Club for 43 years (joining in 1981) and has made a significant contribution to driver training and offroad travel, all the while flying the flag for the Four Wheel Drive Club.</p>
<p>I ask Geoff Joubert to give a brief run down on his life and times.</p>
<p><i>Short presentation by Geoff Joubert</i></p>
<h3><strong>TREASURER'S REPORT AND FINANCIAL STATEMENT FOR 2024 / 2025</strong></h3>
<p><strong>FWDCSA February 2025 Financials</strong></p>
<p>
<ul>
<li>Doug Timm, the Club Treasurer, presented the Treasurers report.</li>
<li>Any member that requires a copy please contact the Club Secretary</li>
<li>Any queries please contact Doug on <a href="mailto:dougtimm12@gmail.com">dougtimm12@gmail.com</a></li>
<li>Proposed: John Runciman Seconded: Peter Hutchison</li>
</ul>
</p>
<h3><strong>NOMINATION AND ELECTION OF COMMITTEE FOR 2025</strong></h3>
<p>I sound like a stuck record, but all the committee members are volunteers that put aside any number of hours a month to ensure that the club runs smoothly, that there are interesting speakers, that trips are organised, that the grounds are maintained, and so on. This is more work than the 5 remaining members of the committee can effectively do. We need help!</p>
<h6>Standing members available for re-election</h6>
<p>
<ul>
<li>John Runciman</li>
<li>Noelene Runciman</li>
<li>Doug Timm</li>
<li>Peter Hutchison</li>
<li>Chris Pinto</li>
<li>Proposed: Mike Hitge Seconded: Zita Harber</li>
</ul>
</p>
<h6>Members resigning from the Committee</h6>
<p>
<ul>
<li>Noel Thompson</li>
<li>Dorota Maskowicz</li>
<li>Dave Nixon</li>
</ul>
</p>
<h6>New members to the Committee</h6>
<p>No one proposed.</p>
<h3><strong>TRIBUTE TO KARL HOFFMAN</strong></h3>
<p>Karl stood down from the position of Club Secretary which he has held for many years. I hand the microphone over the Geoff Joubert for more on this.
<i>Short presentation by Geoff Joubert</i>
</p>
<h3><strong>MOTIONS FOR VOTING</strong></h3>
<p>As I said earlier, Members were given the opportunity to submit proposals for due consideration and have those proposals presented at the AGM. I have asked all the proposers to give a short motivation of their ideas and to answer any questions.</p>
<p>Before they take to the floor, I need to make one point very clear. Whichever proposal or mixture of proposal is adopted, the Membership needs to take on the responsibility to bring it to completion. The Committee will continue to run the Club but will not take on the responsibility of seeing that proposal through.</p>
<p>Each proposal was presented by the proposer.</p>
<h6><strong>PROPOSAL 1 Andrew Maier</strong></h6>
<p>
<ul>
<li>We do not develop Base 4 at all and that we try and sell the property.</li>
<li>Invest the money in a secure investment for 2 years while searching for a new site within 60km of Johannesburg.</li>
<li>Buy and lease to a company for a caravan park/camping ground with exclusive rights for the Club.</li>
</ul>
</p>
<h6><strong>PROPOSAL 2 John Runciman</strong></h6>
<p>
<ul>
<li>Demolish remains and build new structure at the camping site (15x12m steel portal).</li>
<li>3 containers (kitchen, bar, storage), all under roof.</li>
<li>Use bottom entrance as main entrance. Cost: R1,200,000</li>
</ul>
</p>
<h6><strong>PROPOSAL 3 John Runciman</strong></h6>
<p>
<ul>
<li>Similar to current configuration: 12x12m gum/steel structure + 1 container (kitchen/bar/storage).</li>
<li>Use bottom entrance as main entrance. Cost: R450,000</li>
</ul>
</p>
<p><em>Proposals 2 and 3 presented together. Proposal 3 seen as a basis for future development.</em></p>
<h6><strong>PROPOSAL 4 Alan Exton</strong></h6>
<p>
<ul>
<li>Minimum build, prioritize security. Gum pole shed with lean-to roof.</li>
<li>If Proposal 2 adopted, increase container spacing to 1.5m and include adequate toilets.</li>
</ul>
</p>
<h6><strong>PROPOSAL 5 Dorota Maskowicz</strong></h6>
<p>
<ul>
<li>Sell Base 4 ASAP, invest funds.</li>
<li>Open days/weekends continue at various 4x4 and camping venues.</li>
</ul>
</p>
<h6><strong>PROPOSAL 6 Clive Murray</strong></h6>
<p>
<ul>
<li>Remove old structures, place 4 containers on flat ground, add bow roof structure.</li>
<li>Replace camping ablutions with a container. Cost: R1,595,000</li>
</ul>
</p>
<h6><strong>DISCUSSION</strong></h6>
<p>
<ul>
<li>Engineer to assess toilet/kitchen block structure</li>
<li>Base 4 is part of a conservancy: building restrictions</li>
<li>Consider security hub on-site</li>
<li>Maintain swimming pool</li>
</ul>
</p>
<h6><strong>VOTING</strong></h6>
<p>
<ul>
<li><strong>Vote 1:</strong> Sell Base 4 or develop Base 4<br>Sell: 4 Develop: 41 <strong>Vote carried to develop Base 4</strong></li>
<li><strong>Vote 2:</strong> Develop the top (Proposal 6) or bottom (Proposal 3)<br>Top: 12 Bottom: 29 <strong>Vote carried to develop bottom (Proposal 3)</strong></li>
</ul>
</p>
<h3><strong>GENERAL</strong></h3>
<p>Nothing raised</p>
<h3><strong>CLOSING OF MEETING</strong></h3>
<p><strong>Time:</strong> 12h10</p>

View File

@@ -0,0 +1,435 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
$page_id = 'best_0f_ec';
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
</style>
<style>
body {
/* font-family: Arial, sans-serif; */
line-height: 1.6;
/* max-width: 800px; */
margin: auto;
/* padding: 20px; */
}
h1,
h2 {
color: #2c3e50;
}
h2 {
margin-top: 2em;
}
.content {
margin-bottom: 2em;
}
.img-left,
.img-right {
max-width: 30%;
margin: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
.img-left {
float: left;
}
.img-right {
float: right;
}
</style>
<?php
$pageTitle = 'Best of the Eastern Cape 2024';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Blog Detaisl Area start -->
<section class="blog-detaisl-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-8">
<div class="blog-details-content" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<a href="blog.html" class="category">Travel</a>
<ul class="blog-meta mb-30">
<li><img src="assets/images/pp/default.png" alt="Admin"> <a href="#">John Runciman</a></li>
<li><i class="far fa-calendar-alt"></i> <a href="#">25 Feb 2024</a></li>
<li><i class="far fa-comments"></i> <a href="#">Comments (<?= getCommentCount($page_id);?>)</a></li>
</ul>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h2>Best of the Eastern Cape 2024</h2>
<p>Every year, Noelene and I organise a trip through the Eastern Cape, with the highlight being traversing Baviaanskloof. Each trip has been slightly different to the previous one, with this trip, in my opinion, being the best one!</p>
<p class="content">
<img src="assets/images/blog/1/blog_01.jpeg" alt="Bushman's River" class="img-left">
The idea was to meet up at the village at the mouth of the Bushmans River, Boesmansriviermond, near Kenton-on-Sea. Mike and Clara arrived a few days early and we enjoyed a ride up the Bushmans River in our little boat and walks on the beach.<br><br>
The rest of the group—Roy and Naome, Doug and Santie, and Dave and Valery—arrived on the Friday, the day before the official departure. Doug and Dave booked a campsite at Cannon Rocks, 20 or so kilometres from Bushmans.
</p>
<p>We arranged a braai for that evening, and I admit that I was shocked to my little toes when I saw that Doug and Dave had brought a caravan and camping trailer along. This is definitely not a caravan or trailer-friendly route and I voiced my hesitation.</p>
<p>The long and the short was that Doug decided to continue despite my fears, and Dave decided to withdraw from the trip. This was not entirely due to my warnings but also to Valery not feeling up to scratch. We also heard that Roger would not be able to make it because of personal problems at home.</p>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Saturday: Bushmans to Ocean View</h5>
<p class="content">
<img src="assets/images/blog/1/blog_05.jpeg" alt="Fish River Lighthouse" class="img-left">
<p>On Saturday morning, the remaining four vehicles met at Bushmans River with our first destination set for Bathurst for breakfast. We drove via the "poor mans game drive" (the old main road from Port Elizabeth to Port Alfred, now incorporated into the Sibuya Game Reserve) and the winding road through the spectacular Cowie River Valley.</p>
<p>After brunch (the trip took longer than expected due to the bad roads), we wandered along to the Fish River Lighthouse, a place worth a visit. This historic building was erected in the late 19th century with the light first shining on 1 July 1898. The warning light has a strength of 5,000,000 candelas and is 85 metres above the high water mark with a shine range of 32 sea miles. Wish I had that on the front of my Hilux!</p>
<p>The most unique feature about the light is that it has no bearings for the 2 ton light to spin on, but rather it floats in a bed of mercury, ingenious!</p>
</p>
<p class="content">
<img src="assets/images/blog/1/blog_03.jpeg" alt="Baviaanskloof" class="img-right">
<p>From there we drove back past the Bushmans River, towards Boknes (small village on the sea shore), and onto the scenic gravel road going to Alexandria that services all the dairy farms in the area. We turned off the gravel onto a farm road and came out at a camping site, Ocean View, where we arranged to spend 2 nights in amongst the dense Eastern Cape bush on the edge of the sand dunes. This made for a snug campsite sheltered from the wind.</p>
<p>Interest. The location of this campsite is on the eastern edge of the area with the largest shifting dunes in the southern hemisphere. Truly spectacular!</p>
</p>
<div style="width:100%; object-fit: cover;" class="image mt-40 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/blog/1/blog_04.jpeg" alt="Blog Details">
</div>
<div class="clearfix"></div>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Sunday: Beach Day</h5>
<p>The next day was spent exploring the beach—miles and miles of pristine beach where there is not another soul to be seen!</p>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Monday: To Brakkeduine</h5>
<p class="content">
<img src="assets/images/blog/1/blog_06.jpeg" alt="Sand Dunes" class="img-left">
<p>Monday morning, bright and early, we set off towards Port Elizabeth where we planned to leave Max, our faithful hound, for the duration of the trip, then on to Humansdorp and finally to a resort called Brakkeduine.</p>
<p>Doug and Santie, pulling their caravan, suffered a puncture and stopped in the little town of Alexandria to have the tyre repaired and we decided that the remainder would go on in convoy through the thriving metropolis of Port Elizabeth and meet them there.</p>
<p>Once clear of Port Elizabeth, the three remaining vehicles followed the R102, down the old Van Stadens pass, across the single lane bridge spanning the Gamtoos river and past Jeffereys Bay. At Humansdorp we hit the gravel roads eventually reaching Brakkeduine in the late afternoon.</p>
<p>Doug and Santie were already there with Doug trying his hand at fishing in the dam. The campsites are to die for! Set along manicured grassy terraced ledges overlooking the dam, each site separated by neatly trimmed hedges.</p>
<div class="clearfix"></div>
</p>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Tuesday: Dune Adventure</h5>
<p class="content">
<img style="max-width: 45%;" src="assets/images/blog/1/blog_07.jpeg" alt="Sand Dunes" class="img-left">
<p>The following morning we met Johan, our guide for the day. After airing down (0.6 bar!), we set off in convoy to attack the dunes. Before we reached the first dune, Doug pulled a tyre off the rim. We all got stuck in to repair the wheel and were on the road again fifteen minutes later. The airjack proved its usefulness!</p>
<p>We played in the sand for the next few hours, then Roy managed to pull one of his tyres off the rim—on a steep incline and in the boiling heat of the midday sun. This time the airjack did not do so well! We were eventually forced to use Mikes trusty hi-lift jack. Eventually, we changed wheels and headed for camp, then back to Humansdorp to get the wheel repaired.</p>
<p>In the mean time, Doug had also picked up a problem with his Prado, and he and Santie decided to head to the Toyota garage in Joubertina further along the R62 with the plan that we would all meet up again in Kareedouw.</p>
<p>From there, we headed off north into the mountains. The road was not bad, just rocky and plenty of loose stones: I was concerned about the tyres on Dougs Prado and caravan but I need not have worried and we arrived at our camp as the sun was setting.</p>
<p>Baviaans Lodge is situated in the Kouga Mountains at the start of the Rus en Vrede trail across the mountains to the Baviaanskloof. The campsite is cosy, set among the trees on the bank of a small stream. There is a hot water shower and toilets, all well maintained and clean.</p>
<p>We enjoyed an evening around the campfire and I must be honest and say that I went to bed concerned about Doug pulling his caravan over the mountains to Baviaanskloof.</p>
</p>
<div class="clearfix"></div>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Wednesday: Rus en Vrede Trail</h5>
<p>The following morning, I cannot emphasise how important it is to be ready and waiting at the designated time. The day was going to be slow going and I was factoring in time for recovery and vehicle maintenance on the mountain. As it happened, there were no delays of problems whatsoever, just slow going over the rough track. In fact, the only casualty was the awning from Mikes Cruiser which was shaken free and rescued by Roy.</p>
<p class="content">
<img style="max-width: 45%;" src="assets/images/blog/1/blog_08.jpeg" alt="Baviaanskloof" class="img-right">
<p>The Rus en Vrede trail was originally cut across the mountains by the woodcutters back in eighteen something. Now it crosses over three farms, and is a combination of gravel, loose rocks, mountain rocks and eroded farm tracks. There are also 13 gates that had to be opened and closed, thank you Noelene and Naome!</p>
<p>The views cover seven different mountain ranges giving one a panoramic view of the area. We were lucky with the weather, clear skies, no wind, and relatively cool conditions. The proteas were out in full bloom and the famous centuries-old cycads stand guard over the peaks and valleys</p>
</p>
<p class="content">
<img src="assets/images/blog/1/blog_09.jpeg" alt="Sand Dunes" class="img-left">
<p>The trail ends at the Rus en Vrede farm where you pay the farmer per vehicle and per person(details below). Now, onto the main road through the Kloof and a little further we signed in at the entrance to the Baviaanskloof Nature Reserve.</p>
<p>The road twists and turns through the Park with many water crossings, deep ravines and high rugged mountains crossing Holgats Pass, Kombrinks Pass, and the Grootrivier Pass. The roads have not been maintained and the going was slow but the scenery was spectacular. The concrete strip road over the mountain is especially rough with the concrete slabs broken and displaced. This was no problem for our vehicles, in fact this was our preferred route given that we were all driving seriously capable off-road vehicles.</p>
</p>
<p>Our destination was Kudu Kaya, a working citrus farm where we have stayed before, our chosen campsite up on a hill overlooking the farm. Doug had to do a few running repairs on the caravan and Santie worked a good hour cleaning the debris caused to the food supplies being shaken loose by the rough roads: custard and gunk everywhere!</p>
<p>Again, a great evening around the campfire!</p>
<div class="clearfix"></div>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Thursday: Into the Kloof</h5>
<p>Thursday morning saw us on the road to Steytlerville via Antonies Pass, a rugged rock and gravel road with many washaways which takes you down to Antoniesberg and the crossing of the Groot River. Again very slow and careful going.</p>
<p>We stopped at the Royal Hotel in Steytlerville for lunch before pushing on to Kaboega, a private farm in the mountains north of Addo Elephant Park, and sharing a boundary with the Park on the southern border. Here we set up camp at the big dam where we have stayed before. We were met and made welcome by the farm manager, Ian Ritchie and his wife Sandy.</p>
<div style="width:100%; object-fit: cover;" class="image mt-40 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/blog/1/blog_11.jpeg" alt="Blog Details">
</div>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Friday: To Kaboega</h5>
<p>Friday morning Ian and Sandy arrived in camp in time for coffee and while we sipped, Sandy gave us an insight into the Bushmans paintings in the area and a general history of rock paintings in Southern Africa: very interesting stuff! We had arranged for Ian to lead us around the farm, about 6 000 hectares, where he has an intimate knowledge of the plants, the animals, and the terrain. There are seven biodiversitys present here and these diversities have been allowed to flourish with no human intervention for the past 30 to 40 years. We stopped every few kilometres for Ian to show us something, or to tell us a story, or to point out something interesting.</p>
<p class="content">
<img src="assets/images/blog/1/blog_12.jpeg" alt="Sand Dunes" class="img-left">
<p>Apart from a locked gate to the neighbours where we had to cut the chain, there were no hitches as we climbed the mountain, crossing stream after stream, over rocks and through the bushes to the summit of the mountain. Here, we had originally intended to walk to some bushmans paintings in the rocks, but the sun was westering and the decision was made to move on and rather have Ian take us to a swimming hole deep in the mountains before heading for home.</p>
<p>All in all it was a great and informative day and left us all wanting for more. Thank you Ian!</p>
</p>
<div class="clearfix"></div>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Saturday & Sunday: Mountain Zebra Park</h5>
<p>Saturday, on the road again. I planned on taking the secondary gravel roads as far as possible en route to the Mountain Zebra Park via Somerset East and Cradock. We had a delicious breakfast in Somerset East, then followed the road through a giant conservancy before traversing the Swarthoek and Maraiskloof Passes eventually reaching Cradock where we all filled up with fuel.</p>
<p>West out of Cradock, it was a short hop to the Mountain Zebra Park where we booked in, found a campsite, and set up for the night. Originally we planned to spend one night here, allowing anyone that needed to return to Johannesburg for work on Monday morning to travel on the Sunday. Fortunately all decided to stay an extra night, allowing for extensive game drives on the Sunday.</p>
<p class="content">
<img style="max-width: 45%;" src="assets/images/blog/1/blog_13.jpeg" alt="Baviaanskloof" class="img-right">
<p>The Reserve has a wide diversity of plains animals that were seen aplenty on the plateau areas, especially the rare and once nearly extinct mountain zebra, with other animals to be seen in the deep valley and gorges in the area. There are three 4X4 routes, none too challenging, but fun to drive.</p>
<p>We arranged a night drive for the Sunday evening. The weather was turning, cold winds and overcast, so we prepared ourselves with Old Brown Sherry and blankets. This was the middle (almost!) of summer, for goodness sake! The drive was great, buffalo, eland, kudu, you name it. Of great interest were the springhares bouncing along on their hind legs, and 6 porcupines. Sadly we saw no cats or aardwolves.</p>
<p>The drive that was to finish at 21h00 eventually got back to camp at 23h00: thank you to very knowledgeable and generous driver! By now we were frozen solid, back to our campsite for a whiskey and bed. In the morning the temperature gauge on my bakkie read 6 degrees!</p>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Monday: The End</h5>
<p>Monday morning we packed up and went our separate ways. As I always say, sad to leave but happy to be on our way home.</p>
<p>Thank you all that enjoyed the trip with us for all your help, support, friendship and generosity. I am sad that Dave and Verinica, and Roger missed out on a great adventure. Next time!</p>
<div class="clearfix"></div>
</div>
<div data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Trip Information</h5>
<p>All the campsites have the basics of hot water, showers, ablutions etc.</p>
<ul>
<li><strong>Cannon Rocks Caravan Park</strong>
<ul>
<li>Phone: 064 654 0043</li>
<li>75 wind protected sites</li>
<li>Power to each site</li>
<li>Basic supply store</li>
<li>Rates: R370 (low season) to R620 (high season) for 2 people caravan or tent</li>
<li>Also offer pensioner rates and long-stay rates on request</li>
</ul>
</li>
<li><strong>Oceanview Camping</strong>
<ul>
<li>Contact: David & Lynne Cordner</li>
<li>WhatsApp only: 082 573 3660</li>
<li>Power to some campsites</li>
<li>Rates on enquiry</li>
</ul>
</li>
<li><strong>Brakkeduine camping and adventure park</strong>
<ul>
<li>Contact: Bennie & Tania van Niekerk</li>
<li>Phone: 083 657 0601</li>
<li>Email: <a href="mailto:bellakarmabt@gmail.com">bellakarmabt@gmail.com</a></li>
<li>Rates: R100 per person per night, Children R75 PPPN</li>
<li>Power to each campsite</li>
<li>Guided trips: R300 per vehicle (min 5 vehicles)</li>
</ul>
</li>
<li><strong>Baviaans Lodge</strong>
<ul>
<li>Phone: 083 491 1009</li>
<li>Email: <a href="mailto:info@baviaanslodge.co.za">info@baviaanslodge.co.za</a></li>
<li>Rates: Camping R100 per person per night</li>
</ul>
</li>
<li><strong>Rus en Vrede 4X4 Trail</strong>
<ul>
<li>Contact: Chris Lamprecht</li>
<li>Phone: 073 232 8932</li>
<li>Email: <a href="mailto:clamp@igen.co.za">clamp@igen.co.za</a></li>
<li>Website: <a href="http://www.baviaanskloof.co.za">www.baviaanskloof.co.za</a></li>
<li>Fees: R150 per vehicle and R10 per person</li>
</ul>
</li>
<li><strong>Kudu Kaya</strong>
<ul>
<li>Contact: Heloise & Unola</li>
<li>Phone: 087 700 8195</li>
<li>Email: <a href="mailto:info@kudukaya.co.za">info@kudukaya.co.za</a></li>
<li>Rates: Campsite per night R250 (2 people) plus additional adults R70 PPPN</li>
</ul>
</li>
<li><strong>Kaboega</strong>
<ul>
<li>Contacts and rates on enquiry: <a href="mailto:kaboega@jabama.co.za">kaboega@jabama.co.za</a></li>
</ul>
</li>
<li><strong>Mountain Zebra National Park</strong>
<ul>
<li>Email: <a href="mailto:reservations@sanparks.org">reservations@sanparks.org</a></li>
<li>Phone: 012 428 9111</li>
</ul>
</li>
</ul>
</div>
</div>
<hr class="mb-45">
<div class="tag-share mb-50">
<div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<h6>Tags </h6>
<div class="tag-coulds">
<a href="blog.html">Travel</a>
<a href="blog.html">Hotel</a>
<a href="blog.html">Tour</a>
</div>
</div>
<!-- <div class="item" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<h6>Share </h6>
<div class="social-style-one">
<a href="#"><i class="fab fa-facebook-f"></i></a>
<a href="#"><i class="fab fa-twitter"></i></a>
<a href="#"><i class="fab fa-linkedin-in"></i></a>
<a href="#"><i class="fab fa-instagram"></i></a>
</div>
</div> -->
</div>
<!-- <div class="admin-comment bgc-lighter" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="comment-body">
<div class="author-thumb">
<img src="assets/images/blog/admin-comment.jpg" alt="Author">
</div>
<div class="content">
<h4>Richard M. Fudge</h4>
<p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p>
<div class="social-icons">
<a href="contact"><i class="fab fa-facebook-f"></i></a>
<a href="contact"><i class="fab fa-twitter"></i></a>
<a href="contact"><i class="fab fa-linkedin-in"></i></a>
<a href="contact"><i class="fab fa-instagram"></i></a>
</div>
</div>
</div>
</div> -->
<?php include_once('comment_box.php'); ?>
</div>
<div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar">
<!-- <div class="widget widget-search" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="#" class="default-search-form">
<input type="text" placeholder="Search" required="">
<button type="submit" class="searchbutton far fa-search"></button>
</form>
</div> -->
<!-- <div class="widget widget-category" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Category</h5>
<ul class="list-style-three">
<li><a href="blog.html">Adventure</a></li>
<li><a href="blog.html">Hiking & Trekking</a></li>
<li><a href="blog.html">Cycling Tours</a></li>
<li><a href="blog.html">Family Tours</a></li>
<li><a href="blog.html">Mountain Hiking</a></li>
<li><a href="blog.html">Rafting Excursion</a></li>
<li><a href="blog.html">Coastal Paragliding</a></li>
</ul>
</div> -->
<!-- <div class="widget widget-news" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Recent News</h5>
<ul>
<li>
<div class="image">
<img src="assets/images/widgets/news1.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Unique Destinations an tolded Stories ways</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news2.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Immersive Experiences from Around Globe</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news3.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Journey to Inspire Your Next Adventure</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
</ul>
</div> -->
<div class="widget widget-gallery" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Gallery</h5>
<div class="gallery">
<?php
$folder = 'assets/images/blog/1/';
$files = glob($folder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
shuffle($files); // Randomize the order
foreach ($files as $file) {
echo '<a href="' . $file . '" style="width: 110px; height: 110px; overflow: hidden; display: inline-block; margin: 2px;">';
echo '<img src="' . $file . '" alt="Gallery" style="width: 100%; height: 100%; object-fit: cover; display: block;">';
echo '</a>';
}
?>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Blog Detaisl Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

228
src/pages/events/blog.php Normal file
View File

@@ -0,0 +1,228 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
</style><?php
$pageTitle = 'Blogs';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Blog List Area start -->
<section class="blog-list-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-8">
<?php
// Query to retrieve data from blogs table
$status = 'published';
$stmt = $conn->prepare("SELECT blog_id, title, date, category, image, description, author, members_only, link FROM blogs WHERE status = ? ORDER BY date DESC");
$stmt->bind_param("s", $status);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
// Loop through each row
while ($row = $result->fetch_assoc()) {
$blog_id = $row['blog_id'];
$title = $row['title'];
$date = $row['date'];
$category = $row['category'];
$image = $row['image'];
$description = $row['description'];
$author = $row['author'];
$blog_author = $row['author'];
$members_only = $row['members_only'];
if ($members_only) {
if (!isset($_SESSION['user_id'])) {
$blog_link = "login.php";
$button_hover = "Members Only";
$icon = "fa-lock";
} else {
if (getUserMemberStatus($_SESSION['user_id'])) {
$blog_link = $row['link'];
$button_hover = "Read More";
$icon = "fa-arrow-right";
} else {
$blog_link = "#";
$button_hover = "Members Only";
$icon = "fa-lock";
}
}
} else {
$blog_link = $row['link'];
$button_hover = "Read More";
$icon = "fa-arrow-right";
}
// Output the HTML structure with dynamic data
echo '
<div class="blog-item style-three" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img style="border-radius:20px;" src="assets/images/blog/' . $blog_id . '/' . $image . '" alt="Blog List">
</div>
<div class="content">
<a href="' . url('blog') . '" class="category">' . $category . '</a>
<h5><a href="' . $blog_link . '">' . $title . '</a></h5>
<ul class="blog-meta">
<li><i class="far fa-calendar-alt"></i> <a href="#">' . $date . '</a></li>
<li><i class="far fa-user"></i> ' . getFullName($author) . '</li>
</ul>
<p>' . $description . '</p>
<a href="' . $blog_link . '" style="width:100%;" class="theme-btn style-two style-three">
<span style="width:100%;" data-hover="'.$button_hover.'">Read More</span>
<i class="fal '.$icon.'"></i>
</a>
</div>
</div>
';
}
} ?>
<!-- <ul class="pagination pt-15 flex-wrap" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li class="page-item disabled">
<span class="page-link"><i class="far fa-chevron-left"></i></span>
</li>
<li class="page-item active">
<span class="page-link">
1
<span class="sr-only">(current)</span>
</span>
</li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item"><a class="page-link" href="#">...</a></li>
<li class="page-item">
<a class="page-link" href="#"><i class="far fa-chevron-right"></i></a>
</li>
</ul> -->
</div>
<div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar">
<div class="widget widget-search" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="#" class="default-search-form">
<input type="text" placeholder="Search" required="">
<button type="submit" class="searchbutton far fa-search"></button>
</form>
</div>
<!-- <div class="widget widget-category" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Category</h5>
<ul class="list-style-three">
<li><a href="blog.html">Adventure</a></li>
<li><a href="blog.html">Hiking & Trekking</a></li>
<li><a href="blog.html">Cycling Tours</a></li>
<li><a href="blog.html">Family Tours</a></li>
<li><a href="blog.html">Mountain Hiking</a></li>
<li><a href="blog.html">Rafting Excursion</a></li>
<li><a href="blog.html">Coastal Paragliding</a></li>
</ul>
</div> -->
<!-- <div class="widget widget-news" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Recent News</h5>
<ul>
<li>
<div class="image">
<img src="assets/images/widgets/news1.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Unique Destinations an tolded Stories ways</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news2.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Immersive Experiences from Around Globe</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news3.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Journey to Inspire Your Next Adventure</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
</ul>
</div> -->
<div class="widget widget-gallery" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Gallery</h5>
<div class="gallery">
<?php
$folder = 'assets/images/blog/1/';
$files = glob($folder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
shuffle($files); // Randomize the order
foreach ($files as $file) {
echo '<a href="' . $file . '" style="width: 110px; height: 110px; overflow: hidden; display: inline-block; margin: 2px;">';
echo '<img src="' . $file . '" alt="Gallery" style="width: 100%; height: 100%; object-fit: cover; display: block;">';
echo '</a>';
}
?>
</div>
</div>
<div class="widget widget-cta" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="content text-white">
<span class="h6">Explore The World</span>
<h3>Become a Member</h3>
<a href="<?= url('membership') ?>" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Now">Join Now</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
<div class="image">
<img src="assets/images/logos/weblogo.png" alt="CTA">
</div>
<div class="cta-shape"><img src="assets/images/widgets/cta-shape.png" alt="Shape"></div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Blog List Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,533 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 350px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
</style>
<?php
$pageTitle = 'Blog Details';
$breadcrumbs = [['Home' => 'index.php'], ['Blogs' => 'blog.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Blog Detaisl Area start -->
<section class="blog-detaisl-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="blog-details-content" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<a href="blog.html" class="category">Travel</a>
<ul class="blog-meta mb-30">
<li><img src="assets/images/pp/default.png" alt="Admin"> <a href="#">John Runciman</a></li>
<li><i class="far fa-calendar-alt"></i> <a href="#">25 Feb 2024</a></li>
<li><i class="far fa-comments"></i> <a href="#">Comments (5)</a></li>
</ul>
<p>Every year, Noelene and I organise a trip through the Eastern Cape, with the highlight being traversing Baviaanskloof. Each trip has been slightly different to the previous one, with this trip, in my opinion, being the best one!</p>
<p>The idea was to meet up at the village at the mouth of the Bushmans River, Boesmansriviermond, near Kenton-on-Sea. Mike and Clara arrived a few days early and we enjoyed a ride up the Bushmans River in our little boat and walks on the beach.</p>
<p>The rest of the group—Roy and Naome, Doug and Santie, and Dave and Valery—arrived on the Friday, the day before the official departure. Doug and Dave booked a campsite at Cannon Rocks, 20 or so kilometres from Bushmans. We arranged a braai for that evening, and I admit that I was shocked to my little toes when I saw that Doug and Dave had brought a caravan and camping trailer along. This is definitely not a caravan or trailer-friendly route and I voiced my hesitation.</p>
<p>The long and the short was that Doug decided to continue despite my fears, and Dave decided to withdraw from the trip. This was not entirely due to my warnings but also to Valery not feeling up to scratch. We also heard that Roger would not be able to make it because of personal problems at home.</p>
<h5>Saturday: Bushmans to Ocean View</h5>
<p>On Saturday morning, the remaining four vehicles met at Bushmans River with our first destination set for Bathurst for breakfast. We drove via the "poor mans game drive" (the old main road from Port Elizabeth to Port Alfred, now incorporated into the Sibuya Game Reserve) and the winding road through the spectacular Cowie River Valley.</p>
<p>After brunch (the trip took longer than expected due to the bad roads), we wandered along to the Fish River Lighthouse, a place worth a visit. This historic building was erected in the late 19th century with the light first shining on 1 July 1898. The warning light has a strength of 5,000,000 candelas and is 85 metres above the high water mark with a shine range of 32 sea miles. Wish I had that on the front of my Hilux!</p>
<p>The most unique feature about the light is that it has no bearings for the 2-ton light to spin on, but rather it floats in a bed of mercury—ingenious!</p>
<p>From there, we drove back past the Bushmans River, towards Boknes, and onto the scenic gravel road going to Alexandria that services all the dairy farms in the area. We turned off the gravel onto a farm road and came out at a camping site, Ocean View, where we arranged to spend 2 nights amongst the dense Eastern Cape bush on the edge of the sand dunes. This made for a snug campsite sheltered from the wind.</p>
<p><strong>Interest:</strong> The location of this campsite is on the eastern edge of the area with the largest shifting dunes in the southern hemisphere—truly spectacular!</p>
<h5>Sunday: Beach Day</h5>
<p>The next day was spent exploring the beach—miles and miles of pristine beach where there is not another soul to be seen!</p>
<h5>Monday: To Brakkeduine</h5>
<p>Monday morning, bright and early, we set off towards Port Elizabeth where we planned to leave Max, our faithful hound, for the duration of the trip, then on to Humansdorp and finally to a resort called Brakkeduine. Doug and Santie, pulling their caravan, suffered a puncture and stopped in the little town of Alexandria to have the tyre repaired. We decided that the remainder would go on in convoy through Port Elizabeth and meet them there.</p>
<p>Once clear of Port Elizabeth, the three remaining vehicles followed the R102, down the old Van Stadens Pass, across the single lane bridge spanning the Gamtoos River, and past Jeffereys Bay. At Humansdorp, we hit the gravel roads and eventually reached Brakkeduine in the late afternoon. Doug and Santie were already there, with Doug trying his hand at fishing in the dam. The campsites are to die for—set along manicured grassy terraced ledges overlooking the dam, each site separated by neatly trimmed hedges.</p>
<h5>Tuesday: Dune Adventure</h5>
<p>The following morning we met Johan, our guide for the day. After airing down (0.6 bar!), we set off in convoy to attack the dunes. Before we reached the first dune, Doug pulled a tyre off the rim. We all got stuck in to repair the wheel and were on the road again fifteen minutes later. The airjack proved its usefulness!</p>
<p>We played in the sand for the next few hours, then Roy managed to pull one of his tyres off the rim—on a steep incline and in the boiling heat of the midday sun. This time the airjack did not do so well! We were eventually forced to use Mikes trusty hi-lift jack. Eventually, we changed wheels and headed for camp, then back to Humansdorp to get the wheel repaired.</p>
<p>Doug had also picked up a problem with his Prado, and he and Santie decided to head to the Toyota garage in Joubertina, further along the R62, with the plan that we would all meet up again in Kareedouw.</p>
<h5>Wednesday: Rus en Vrede Trail</h5>
<p>From Kareedouw, we headed off north into the mountains. The road was rocky and full of loose stones. I was concerned about the tyres on Dougs Prado and caravan, but we arrived at our camp as the sun was setting. Baviaans Lodge is situated in the Kouga Mountains at the start of the Rus en Vrede trail across the mountains to the Baviaanskloof. The campsite is cosy, set among the trees on the bank of a small stream. There is a hot water shower and toilets, all well maintained and clean.</p>
<p>We enjoyed an evening around the campfire, though I went to bed concerned about Doug pulling his caravan over the mountains.</p>
<p>On Wednesday morning, everyone was packed and ready to go by 08:00. The day was slow going but with no delays or problems. The only casualty was the awning from Mikes Cruiser, which was shaken free and rescued by Roy.</p>
<p>The Rus en Vrede trail, originally cut by woodcutters in the 1800s, now crosses three farms. It consists of gravel, loose rocks, eroded farm tracks, and mountain terrain. There are 13 gates that had to be opened and closed—thank you Noelene and Naome!</p>
<p>The views are breathtaking, covering seven different mountain ranges. We were lucky with the weather—clear skies, no wind, and cool temperatures. The proteas were in bloom and the centuries-old cycads stood tall over the peaks.</p>
<h5>Thursday: Into the Kloof</h5>
<p>The trail ends at Rus en Vrede farm, where you pay the farmer per vehicle and person. We entered the Baviaanskloof Nature Reserve, crossing Holgats Pass, Kombrinks Pass, and the Grootrivier Pass. The roads were rough and slow-going but scenic.</p>
<p>Our destination was Kudu Kaya, a working citrus farm. We camped on a hill overlooking the farm. Doug did some repairs to the caravan and Santie spent time cleaning up food shaken loose—custard and gunk everywhere!</p>
<h5>Friday: To Kaboega</h5>
<p>Thursday morning, we drove to Steytlerville via Antonies Pass—a rugged rock and gravel road. After lunch at the Royal Hotel in Steytlerville, we continued to Kaboega, a private farm near Addo Elephant Park. We camped at a big dam and were warmly welcomed by Ian Ritchie and his wife Sandy.</p>
<p>Friday morning, Ian and Sandy joined us for coffee. Sandy shared insights into Bushmans paintings and local history. Ian then led us around the 6,000-hectare farm, sharing his deep knowledge of biodiversity, plants, and terrain. Apart from a locked gate we had to cut open, the day was smooth. We ended with a swim in a mountain pool instead of visiting more rock art sites due to the time.</p>
<h5>Saturday: Mountain Zebra Park</h5>
<p>On Saturday, we took scenic gravel roads to the Mountain Zebra Park via Somerset East and Cradock. After breakfast in Somerset East, we passed through Swarthoek and Maraiskloof Passes to Cradock for fuel, then entered the Park and set up camp.</p>
<p>Though we originally planned to stay one night, everyone decided to stay an extra day for game drives. The reserve is home to a wide range of plains animals, especially the rare mountain zebra, and other wildlife found in the gorges and valleys.</p>
<div style="width:100%; object-fit: cover;" class="image mt-40 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/blog/1/widecrab.jpg" alt="Blog Details">
</div>
<h5>Services Offered by a Tour and Travel Agency</h5>
<p>Agency plays a pivotal role in crafting memorable experiences for travelers by offering wide range services tailored to individual preferences. Whether it's a family vacation, an adventure trip, or luxury getaway well-established travel agency can handle everything from flight bookings and accommodation to guided tours .</p>
<ul class="list-style-two mt-30 mb-45" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li>Assisting customers in booking domestic and international flights.</li>
<li>Organizing adventure activities such as trekking, diving, safaris, or extreme sports.</li>
<li>Tailoring travel plans to meet the specific needs and preferences of the customer.</li>
<li>Providing professional guides for city tours, cultural experiences, adventure activities, etc.</li>
<li>Arranging local transportation such as car rentals, airport transfers, or bus tours.</li>
<li>Helping customers navigate the visa application process for international travel.</li>
</ul>
<div class="row mb-10">
<div class="col-sm-6">
<div class="image mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/blog/blog-middle1.jpg" alt="Blog">
</div>
</div>
<div class="col-sm-6">
<div class="image mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="50">
<img src="assets/images/blog/blog-middle2.jpg" alt="Blog">
</div>
</div>
</div>
<h5>How to Start a Tour and Travel Agency</h5>
<p>Agency plays a pivotal role in crafting memorable experiences for travelers by offering wide range services tailored to individual preferences. Whether it's a family vacation, an adventure trip, or luxury getaway well-established travel agency can handle everything from flight bookings and accommodation to guided tours .</p>
<blockquote class="mt-30 mb-35" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<i class="flaticon-quote"></i>
<div class="text">"In the world of tours and travel, every journey is an invitation to explore the unknown, connect with cultures, and create memories that last lifetime It's not just about the destination,extraordinary adventures."
</div>
<div class="blockquote-footer">
Kevin F. Glasscock
</div>
</blockquote>
<ul class="list-style-two mb-45" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li>Understand the demand in your area, competition, and potential customers.</li>
<li>Register your business, obtain necessary licenses, and ensure compliance with local regulations.</li>
<li>Build relationships with hotels, airlines, transport companies, and other service providers.</li>
</ul>
</div>
<hr class="mb-45">
<div class="tag-share mb-50">
<div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<h6>Tags </h6>
<div class="tag-coulds">
<a href="blog.html">Travel</a>
<a href="blog.html">Hotel</a>
<a href="blog.html">Tour</a>
</div>
</div>
<div class="item" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<h6>Share </h6>
<div class="social-style-one">
<a href="#"><i class="fab fa-facebook-f"></i></a>
<a href="#"><i class="fab fa-twitter"></i></a>
<a href="#"><i class="fab fa-linkedin-in"></i></a>
<a href="#"><i class="fab fa-instagram"></i></a>
</div>
</div>
</div>
<div class="admin-comment bgc-lighter" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="comment-body">
<div class="author-thumb">
<img src="assets/images/blog/admin-comment.jpg" alt="Author">
</div>
<div class="content">
<h4>Richard M. Fudge</h4>
<p>The world is a book, and those who do not travel read only one page. Every journey we undertake is a chapter filled with lessons, experiences, and stories.</p>
<div class="social-icons">
<a href="contact"><i class="fab fa-facebook-f"></i></a>
<a href="contact"><i class="fab fa-twitter"></i></a>
<a href="contact"><i class="fab fa-linkedin-in"></i></a>
<a href="contact"><i class="fab fa-instagram"></i></a>
</div>
</div>
</div>
</div>
<div class="next-prev-blog pt-70 pb-15">
<div class="item" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/blog/prev-post.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Unique Destinations an tolded Stories ways</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</div>
<div class="item" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/blog/next-post.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Immersive Experiences from Around Globe</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</div>
</div>
<form id="comment-form" class="comment-form bgc-lighter z-1 rel mt-25" name="review-form" action="#" method="post" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5>Leave A Comments</h5>
<p>Your email address will not be published. Required fields are marked *</p>
<div class="row gap-20 mt-30">
<div class="col-md-6">
<div class="form-group">
<input type="text" id="full-name" name="full-name" class="form-control" placeholder="Name" value="" required="">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<input type="email" id="email-address" name="email" class="form-control" placeholder="Email" value="" required="">
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<textarea name="message" id="message" class="form-control" rows="5" placeholder="Message" required=""></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<ul class="radio-filter mb-25">
<li>
<input class="form-check-input" type="radio" name="terms-condition" id="terms-condition">
<label for="terms-condition">Save my name, email, and website in this browser for the next time I comment.</label>
</li>
</ul>
<button type="submit" class="theme-btn style-two">
<span data-hover="Send Comments">Send Comments</span>
<i class="fal fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</form>
</div>
<!-- <div class="col-lg-4 col-md-8 col-sm-10 rmt-75">
<div class="blog-sidebar">
<div class="widget widget-search" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="#" class="default-search-form">
<input type="text" placeholder="Search" required="">
<button type="submit" class="searchbutton far fa-search"></button>
</form>
</div>
<div class="widget widget-category" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Category</h5>
<ul class="list-style-three">
<li><a href="blog.html">Adventure</a></li>
<li><a href="blog.html">Hiking & Trekking</a></li>
<li><a href="blog.html">Cycling Tours</a></li>
<li><a href="blog.html">Family Tours</a></li>
<li><a href="blog.html">Mountain Hiking</a></li>
<li><a href="blog.html">Rafting Excursion</a></li>
<li><a href="blog.html">Coastal Paragliding</a></li>
</ul>
</div>
<div class="widget widget-news" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Recent News</h5>
<ul>
<li>
<div class="image">
<img src="assets/images/widgets/news1.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Unique Destinations an tolded Stories ways</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news2.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Immersive Experiences from Around Globe</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
<li>
<div class="image">
<img src="assets/images/widgets/news3.jpg" alt="News">
</div>
<div class="content">
<h6><a href="blog-details.html">Journey to Inspire Your Next Adventure</a></h6>
<span class="date"><i class="far fa-calendar-alt"></i> 25 Feb 2024</span>
</div>
</li>
</ul>
</div>
<div class="widget widget-gallery" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h5 class="widget-title">Gallery</h5>
<div class="gallery">
<a href="assets/images/widgets/gallery1.jpg">
<img src="assets/images/widgets/gallery1.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery2.jpg">
<img src="assets/images/widgets/gallery2.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery3.jpg">
<img src="assets/images/widgets/gallery3.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery4.jpg">
<img src="assets/images/widgets/gallery4.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery5.jpg">
<img src="assets/images/widgets/gallery5.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery6.jpg">
<img src="assets/images/widgets/gallery6.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery7.jpg">
<img src="assets/images/widgets/gallery7.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery8.jpg">
<img src="assets/images/widgets/gallery8.jpg" alt="Gallery">
</a>
<a href="assets/images/widgets/gallery9.jpg">
<img src="assets/images/widgets/gallery9.jpg" alt="Gallery">
</a>
</div>
</div>
<div class="widget widget-cta" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="content text-white">
<span class="h6">Explore The World</span>
<h3>Best Tourist Place</h3>
<a href="tour-grid.html" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Now">Explore Now</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
<div class="image">
<img src="assets/images/widgets/cta-widget.png" alt="CTA">
</div>
<div class="cta-shape"><img src="assets/images/widgets/cta-shape.png" alt="Shape"></div>
</div>
</div>
</div> -->
</div>
</div>
</section>
<!-- Blog Detaisl Area end -->
<!-- footer area start -->
<footer class="main-footer footer-two bgc-black rel z-15">
<div class="container">
<div class="footer-instagram pt-100">
<div class="row row-cols-xxl-6 row-cols-xl-5 row-cols-lg-4 row-cols-md-3 row-cols-2">
<div class="col" data-aos="zoom-in-up" data-aos-duration="1500" data-aos-offset="50">
<a class="instagram-item" href="assets/images/instagram/instagram1.jpg">
<img src="assets/images/instagram/instagram1.jpg" alt="Instagram">
</a>
</div>
<div class="col" data-aos="zoom-in-down" data-aos-duration="1500" data-aos-offset="50">
<a class="instagram-item" href="assets/images/instagram/instagram2.jpg">
<img src="assets/images/instagram/instagram2.jpg" alt="Instagram">
</a>
</div>
<div class="col" data-aos="zoom-in-up" data-aos-duration="1500" data-aos-offset="50">
<a class="instagram-item" href="assets/images/instagram/instagram3.jpg">
<img src="assets/images/instagram/instagram3.jpg" alt="Instagram">
</a>
</div>
<div class="col" data-aos="zoom-in-down" data-aos-duration="1500" data-aos-offset="50">
<a class="instagram-item" href="assets/images/instagram/instagram4.jpg">
<img src="assets/images/instagram/instagram4.jpg" alt="Instagram">
</a>
</div>
<div class="col" data-aos="zoom-in-up" data-aos-duration="1500" data-aos-offset="50">
<a class="instagram-item" href="assets/images/instagram/instagram5.jpg">
<img src="assets/images/instagram/instagram5.jpg" alt="Instagram">
</a>
</div>
<div class="col" data-aos="zoom-in-down" data-aos-duration="1500" data-aos-offset="50">
<a class="instagram-item" href="assets/images/instagram/instagram6.jpg">
<img src="assets/images/instagram/instagram6.jpg" alt="Instagram">
</a>
</div>
</div>
</div>
</div>
<div class="widget-area bgp-bottom pt-70 pb-130 rpb-50" style="background-image: url(assets/images/backgrounds/footer-two.png);">
<div class="container">
<div class="row row-cols-xxl-5 row-cols-xl-4 row-cols-md-3 row-cols-2">
<div class="col col-small" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-text">
<div class="footer-logo mb-40">
<a href="index.html"><img src="assets/images/logos/logo.png" alt="Logo"></a>
</div>
<div class="footer-map">
<iframe src="https://www.google.com/maps/embed?pb=!1m10!1m8!1m3!1d96777.16150026117!2d-74.00840582560909!3d40.71171357405996!3m2!1i1024!2i768!4f13.1!5e0!3m2!1sen!2sbd!4v1706508986625!5m2!1sen!2sbd" style="border:0; width: 100%;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
</div>
</div>
<div class="col col-small" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-links ms-sm-5">
<div class="footer-title">
<h5>Services</h5>
</div>
<ul class="list-style-three">
<li><a href="destination-details.html">Best Tour Guide</a></li>
<li><a href="destination-details.html">Tour Booking</a></li>
<li><a href="destination-details.html">Hotel Booking</a></li>
<li><a href="destination-details.html">Ticket Booking</a></li>
</ul>
</div>
</div>
<div class="col col-small" data-aos="fade-up" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-links ms-md-4">
<div class="footer-title">
<h5>Company</h5>
</div>
<ul class="list-style-three">
<li><a href="about.html">About Company</a></li>
<li><a href="blog.html">Community Blog</a></li>
<li><a href="contact">Jobs and Careers</a></li>
<li><a href="blog.html">latest News Blog</a></li>
</ul>
</div>
</div>
<div class="col col-small" data-aos="fade-up" data-aos-delay="150" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-links ms-lg-4">
<div class="footer-title">
<h5>Destinations</h5>
</div>
<ul class="list-style-three">
<li><a href="destination-details.html">African Safaris</a></li>
<li><a href="destination-details.html">Alaska & Canada</a></li>
<li><a href="destination-details.html">South America</a></li>
<li><a href="destination-details.html">Middle East</a></li>
</ul>
</div>
</div>
<div class="col col-md-6 col-10 col-small" data-aos="fade-up" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-contact">
<div class="footer-title">
<h5>Get In Touch</h5>
</div>
<ul class="list-style-one">
<li><i class="fal fa-map-marked-alt"></i> 578 Level, D-block 45 Street Melbourne, Australia</li>
<li><i class="fal fa-envelope"></i> <a href="mailto:supportrevelo@gmail.com">supportrevelo @gmail.com</a></li>
<li><i class="fal fa-phone-volume"></i> <a href="callto:+88012334588">+880 (123) 345 88</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="footer-bottom pt-20 pb-5">
<div class="container">
<div class="row">
<div class="col-lg-5">
<div class="copyright-text text-center text-lg-start">
<p>@Copy 2024 <a href="index.html">Ravelo</a>, All rights reserved</p>
</div>
</div>
<div class="col-lg-7 text-center text-lg-end">
<ul class="footer-bottom-nav">
<li><a href="about.html">Terms</a></li>
<li><a href="about.html">Privacy Policy</a></li>
<li><a href="about.html">Legal notice</a></li>
<li><a href="about.html">Accessibility</a></li>
</ul>
</div>
</div>
<!-- Scroll Top Button -->
<button class="scroll-top scroll-to-target" data-target="html"><img src="assets/images/icons/scroll-up.png" alt="Scroll Up"></button>
</div>
</div>
</footer>
<!-- footer area end -->
</div>
<!--End pagewrapper-->
<!-- Jquery -->
<script src="assets/js/jquery-3.6.0.min.js"></script>
<!-- Bootstrap -->
<script src="assets/js/bootstrap.min.js"></script>
<!-- Appear Js -->
<script src="assets/js/appear.min.js"></script>
<!-- Slick -->
<script src="assets/js/slick.min.js"></script>
<!-- Magnific Popup -->
<script src="assets/js/jquery.magnific-popup.min.js"></script>
<!-- Nice Select -->
<script src="assets/js/jquery.nice-select.min.js"></script>
<!-- Image Loader -->
<script src="assets/js/imagesloaded.pkgd.min.js"></script>
<!-- Jquery UI -->
<script src="assets/js/jquery-ui.min.js"></script>
<!-- Isotope -->
<script src="assets/js/isotope.pkgd.min.js"></script>
<!-- AOS Animation -->
<script src="assets/js/aos.js"></script>
<!-- Custom script -->
<script src="assets/js/script.js"></script>
</body>
</html>

196
src/pages/events/events.php Normal file
View File

@@ -0,0 +1,196 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 320px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
.custom-modal {
display: none;
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.9);
}
.custom-modal-content {
margin: 5% auto;
padding: 20px;
max-width: 800px;
text-align: center;
background: #fff;
border-radius: 10px;
position: relative;
}
.custom-modal-content img {
max-width: 100%;
height: auto;
border-radius: 5px;
}
</style>
<?php
$pageTitle = 'Events';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Tour List Area start -->
<section class="tour-list-page py-100 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-12">
<div class="shop-shorter rel z-3 mb-20">
<!-- <ul class="grid-list mb-15 me-2">
<li><a href="#"><i class="fal fa-border-all"></i></a></li>
<li><a href="#"><i class="far fa-list"></i></a></li>
</ul>
<div class="sort-text mb-15 me-4 me-xl-auto">
</div> -->
<div class="sort-text mb-15 me-4">
Sort By
</div>
<select>
<option value="default" selected="">Sort By</option>
<option value="new">Newness</option>
<option value="old">Oldest</option>
<option value="hight-to-low">High To Low</option>
<option value="low-to-high">Low To High</option>
</select>
</div>
<?php
// Query to retrieve upcoming events
$stmt = $conn->prepare("SELECT event_id, date, time, name, image, description, feature, location, type, promo FROM events WHERE date > CURDATE() ORDER BY date ASC");
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
// Loop through each row
while ($row = $result->fetch_assoc()) {
$event_id = $row['event_id'];
$date = $row['date'];
$time = $row['time'];
$name = $row['name'];
$image = $row['image'];
$description = $row['description'];
$feature = $row['feature'];
$location = $row['location'];
$type = $row['type'];
$promo = $row['promo'];
// Determine the badge text based on the status
$badge_text = 'OPEN DAY';
// Output the HTML structure with dynamic data
echo '
<div class="destination-item style-three bgc-lighter" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="' . $image . '" alt="' . $name . '">
</div>
<div class="content">
<div class="destination-header">
<span class="location"><i class="fal fa-map-marker-alt"></i> ' . $location . '</span>
</div>
<h5>' . $name . '</a></h5>
<p>' . $feature . '</p>
<p>' . $description . '</p>
<ul class="blog-meta">
<li><i class="far fa-calendar"></i> ' . convertDate($date) . '</li>
<li><i class="far fa-clock"></i> ' . $time . '</li>
</ul>
<button type="button" class="theme-btn style-three view-image-btn" style="padding: 2px 20px"
data-image-src="' . $promo . '"
data-image-title="' . htmlspecialchars($name, ENT_QUOTES) . '">
View Promo
</button>
</div>
</div>';
}
} else {
echo "No events available.";
}
// Close connection
$conn->close();
?>
</div>
</div>
</div>
</section>
<!-- Tour List Area end -->
<!-- Custom Image Modal -->
<div id="customImageModal" class="custom-modal">
<div class="custom-modal-content">
<span class="custom-modal-close">&times;</span>
<h5 id="modalImageTitle"></h5>
<img id="modalImageElement" src="" alt="" class="img-fluid">
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function() {
const modal = document.getElementById("customImageModal");
const modalImg = document.getElementById("modalImageElement");
const modalTitle = document.getElementById("modalImageTitle");
const closeBtn = document.querySelector(".custom-modal-close");
document.querySelectorAll(".view-image-btn").forEach(button => {
button.addEventListener("click", () => {
const src = button.getAttribute("data-image-src");
const title = button.getAttribute("data-image-title");
modalImg.src = src;
modalTitle.textContent = title;
modal.style.display = "block";
});
});
closeBtn.addEventListener("click", () => {
modal.style.display = "none";
modalImg.src = "";
});
// Optional: click outside modal to close
window.addEventListener("click", (e) => {
if (e.target === modal) {
modal.style.display = "none";
modalImg.src = "";
}
});
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,55 @@
<?php
// Instagram Access Token
$accessToken = 'IGQWRPZADdZAUGtMM2NyZADJVSERoSUZA6bVVBV3lKY0dJbWlEcGVvaVYySDViMHMwSkFBQXRVWVQ1ZA080MkRJd0pzYndjWFVTRmtrenBvRml6Vy03NGZAPQ1pROWNKVkJoMTZAid0lIM2htejc3czByajYwMmJHWTBMMTgZD';
// Instagram API URL to fetch recent media posts
$apiUrl = 'https://graph.instagram.com/me/media?fields=id,caption,media_url,thumbnail_url,permalink,media_type&limit=6&access_token=' . $accessToken;
// Initialize cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
curl_close($ch);
// Decode the JSON response
$instaFeed = json_decode($response);
?>
<style>
.insta-post {
display: inline-block;
margin: 10px;
}
.insta-post img {
width: 200px;
height: 200px;
overflow: hidden;
border-radius: 10px;
}
</style>
<div class="container">
<div class="footer-instagram pt-100">
<div class="row row-cols-xxl-6 row-cols-xl-5 row-cols-lg-4 row-cols-md-3 row-cols-2">
<?php
// echo "TEST";
// Loop through the media and display them
if (!empty($instaFeed->data)) {
foreach ($instaFeed->data as $post) {
// Check if it's an image post
if ($post->media_type == 'IMAGE' || $post->media_type == 'CAROUSEL_ALBUM') {
echo "<div class='col' data-aos='zoom-in-up' data-aos-duration='1500' data-aos-offset='50'>";
echo "<a class='insta-post' href='" . $post->permalink . "' target='_blank'>";
echo "<img src='" . $post->media_url . "' alt='" . htmlspecialchars($post->caption) . "'>";
echo "</a>";
echo "</div>";
}
}
}
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,281 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkAdmin();
if (!isset($_GET['token']) || empty($_GET['token'])) {
die("Invalid request.");
}
$token = $_GET['token'];
// echo $token;
// Use ?user_id=... in the URL to view another user's info
$viewing_user_id = isset($_GET['token']) ? decryptData($token, $salt) : $_SESSION['user_id'];
checkMembershipApplication2($viewing_user_id);
// Fetch membership details
$sql = "SELECT membership_start_date, membership_end_date, payment_status, payment_amount, payment_id FROM membership_fees WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $viewing_user_id);
$stmt->execute();
$result = $stmt->get_result();
$membership = $result->fetch_assoc();
// Fetch application data
$query = "SELECT * FROM membership_application WHERE user_id = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("i", $viewing_user_id);
$stmt->execute();
$result = $stmt->get_result();
$application = $result->fetch_assoc();
$stmt->close();
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
</style>
<section class="account-settings-area py-70 rel z-1">
<div class="container">
<button onclick="downloadMembershipPDF()">📄 Open as PDF</button>
<div class="row align-items-center">
<div class="col-lg-12">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<div id="membership-info">
<div class="section-title py-20">
<h2>Member Information: <?php echo getFullName($viewing_user_id); ?></h2>
</div>
<div style='padding:10px;'>
<table>
<thead>
<tr>
<th>Start Date</th>
<th>Renewal Date</th>
<th>Indemnity</th>
<th>Amount</th>
<th>Payment Reference</th>
<th>Payment Status</th>
<th>Membership Status</th>
</tr>
</thead>
<tbody>
<?php if ($membership): ?>
<tr>
<td><?php echo htmlspecialchars($membership['membership_start_date']); ?></td>
<td><?php echo htmlspecialchars($membership['membership_end_date']); ?></td>
<td><?php echo hasAcceptedIndemnity($viewing_user_id) ? 'SIGNED' : 'NOT SIGNED'; ?></td>
<td><?php echo htmlspecialchars($membership['payment_amount']); ?></td>
<td><?php echo htmlspecialchars($membership['payment_id']); ?></td>
<td><?php echo htmlspecialchars($membership['payment_status']); ?></td>
<td><?php echo getUserMemberStatus($viewing_user_id) ? 'ACTIVE' : 'INACTIVE'; ?></td>
</tr>
<?php else: ?>
<tr>
<td colspan="7">No membership records found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<h3>Main Member</h3>
<div class="row mt-35">
<?php
$fields = [
'first_name' => 'First Name',
'last_name' => 'Surname',
'id_number' => 'ID Number / Passport Number',
'dob' => 'Date of Birth',
'occupation' => 'Occupation',
'tel_cell' => 'Cell Phone',
'email' => 'Email Address'
];
foreach ($fields as $key => $label): ?>
<div class="col-md-6">
<div class="form-group">
<label><?php echo $label; ?></label>
<p class="form-control-static"><?php echo htmlspecialchars($application[$key] ?? ''); ?></p>
</div>
</div>
<?php endforeach; ?>
</div>
<h3>Spouse / Life Partner / Other Details</h3>
<div class="row mt-35">
<?php
$spouse_fields = [
'spouse_first_name' => 'First Name',
'spouse_last_name' => 'Surname',
'spouse_id_number' => 'ID Number / Passport Number',
'spouse_dob' => 'Date of Birth',
'spouse_occupation' => 'Occupation',
'spouse_tel_cell' => 'Cell Phone',
'spouse_email' => 'Email Address'
];
foreach ($spouse_fields as $key => $label): ?>
<div class="col-md-6">
<div class="form-group">
<label><?php echo $label; ?></label>
<p class="form-control-static"><?php echo htmlspecialchars($application[$key] ?? ''); ?></p>
</div>
</div>
<?php endforeach; ?>
</div>
<h3>Children's Names</h3>
<div class="row mt-35">
<?php for ($i = 1; $i <= 3; $i++): ?>
<div class="col-md-6">
<div class="form-group">
<label>Child <?php echo $i; ?> Name</label>
<p class="form-control-static"><?php echo htmlspecialchars($application['child_name' . $i] ?? ''); ?></p>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Child <?php echo $i; ?> DOB</label>
<p class="form-control-static"><?php echo htmlspecialchars($application['child_dob' . $i] ?? ''); ?></p>
</div>
</div>
<?php endfor; ?>
</div>
<h3>Address</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label>Physical Address</label>
<p class="form-control-static"><?php echo nl2br(htmlspecialchars($application['physical_address'] ?? '')); ?></p>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Postal Address</label>
<p class="form-control-static"><?php echo nl2br(htmlspecialchars($application['postal_address'] ?? '')); ?></p>
</div>
</div>
</div>
<h3>Interests and Hobbies</h3>
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<p class="form-control-static"><?php echo nl2br(htmlspecialchars($application['interests_hobbies'] ?? '')); ?></p>
</div>
</div>
</div>
<h3>Primary Vehicle</h3>
<div class="row mt-35">
<?php
$vehicle_fields = [
'vehicle_make' => 'Make',
'vehicle_model' => 'Model',
'vehicle_year' => 'Year',
'vehicle_registration' => 'Registration'
];
foreach ($vehicle_fields as $key => $label): ?>
<div class="col-md-3">
<div class="form-group">
<label><?php echo $label; ?></label>
<p class="form-control-static"><?php echo htmlspecialchars($application[$key] ?? ''); ?></p>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- You can add secondary vehicle and other custom sections in the same way -->
</div>
</div>
</div>
</div>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<script>
function downloadMembershipPDF() {
const element = document.getElementById('membership-info');
// Temporarily shrink element for PDF
element.style.transform = 'scale(0.8)';
element.style.transformOrigin = 'top left';
const opt = {
margin: 0.5,
filename: 'membership-info.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2 },
jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
};
html2pdf().from(element).set(opt).outputPdf('bloburl').then((pdfUrl) => {
window.open(pdfUrl, '_blank');
// Restore original size
element.style.transform = '';
element.style.transformOrigin = '';
});
}
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,73 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
// Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id']) && isset($conn) && $conn !== null) {
$user_id = $_SESSION['user_id'];
// Fetch user data from the database
$sql = "SELECT * FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
} else {
$user = null;
}
?><?php
$pageTitle = 'Membership';
$breadcrumbs = [['Home' => 'index.php']];
require_once(dirname(dirname(dirname(__DIR__))) . '/components/banner.php');
?>
<!-- Contact Form Area start -->
<section class="about-us-area py-100 rpb-90 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-xl-5 col-lg-6">
<div class="about-us-content rmb-55" data-aos="fade-left" data-aos-duration="1500"
data-aos-offset="50">
<div class="section-title mb-25">
<h2>Become a member of 4WDCSA</h2>
<p>Sign up for an annual membership and receive:</p>
<ul class="list-style-two mt-35 mb-30">
<li>Year round access to BASE4</li>
<li>FREE Camping at BASE4</li>
<li>Up to 95% Discount on Training Courses</li>
<li>Exclusive Member discounts for all trips and events</li>
<li>... and many more!</li>
</ul>
</div>
<h2>R 2,500/year</h2>
<p>We go above and beyond to make your travel dreams reality hidden gems and must-see
attractions</p>
<a href="membership_application" class="theme-btn mt-10 style-two">
<span data-hover="Start Application">Start Application</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
<div class="col-xl-7 col-lg-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="about-us-image">
<!-- <div class="shape"><img src="assets/images/about/shape1.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape2.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape3.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape4.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape5.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape6.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape7.png" alt="Shape"></div> -->
<img src="assets/images/logos/weblogo.png" alt="About">
</div>
</div>
</div>
</div>
</section>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,284 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession();
// Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
}else{
header('Location: login.php');
exit(); // Stop further script execution
}
checkMembershipApplication($user_id);
// Fetch user data from the database
$sql = "SELECT * FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
?><?php
$pageTitle = 'Membership Application';
$breadcrumbs = [['Home' => 'index.php'], ['Membership' => 'membership.php']];
require_once($rootPath . '/components/banner.php');
?>
<section class="contact-form-area py-70 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-12">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="registerForm" name="registerForm" action="process_application" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="section-title">
<div id="responseMessage"></div> <!-- Message display area -->
</div>
<!-- Personal Details Section -->
<h3>Main Member</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="first_name">First Name*</label>
<input type="text" id="first_name" name="first_name" class="form-control" placeholder="John" value="<?php echo $user['first_name']; ?>" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="last_name">Surname*</label>
<input type="text" id="last_name" name="last_name" class="form-control" placeholder="Smith" value="<?php echo $user['last_name']; ?>" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="id_number">ID Number / Passport Number*</label>
<input type="text" id="id_number" name="id_number" class="form-control" placeholder="1234567890" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="dob">Date of Birth*</label>
<input type="date" id="dob" name="dob" class="form-control" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="occupation">Occupation*</label>
<input type="text" id="occupation" name="occupation" class="form-control" placeholder="Occupation" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="phone_numbers">Cell Phone*</label>
<input type="text" id="tel_cell" name="tel_cell" class="form-control" placeholder="Cell Phone" value="<?php echo $user['phone_number']; ?>" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="email">Email Address*</label>
<input type="email" id="email" name="email" class="form-control" placeholder="Enter email" value="<?php echo $user['email']; ?>" required>
</div>
</div>
</div>
<!-- Spouse / Partner Details Section -->
<h3>Spouse / Life Partner / Other Details</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="spouse_first_name">First Name</label>
<input type="text" id="spouse_first_name" name="spouse_first_name" class="form-control" placeholder="Jane">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="spouse_last_name">Surname</label>
<input type="text" id="spouse_last_name" name="spouse_last_name" class="form-control" placeholder="Smith">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="spouse_id_number">ID Number / Passport Number</label>
<input type="text" id="spouse_id_number" name="spouse_id_number" class="form-control" placeholder="1234567890">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="spouse_dob">Date of Birth</label>
<input type="date" id="spouse_dob" name="spouse_dob" class="form-control">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="spouse_occupation">Occupation</label>
<input type="text" id="spouse_occupation" name="spouse_occupation" class="form-control" placeholder="Occupation">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="spouse_phone_numbers">Cell Phone</label>
<input type="text" id="spouse_tel_cell" name="spouse_tel_cell" class="form-control" placeholder="Cell Phone">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="spouse_email">Email Address</label>
<input type="email" id="spouse_email" name="spouse_email" class="form-control" placeholder="Enter email">
</div>
</div>
</div>
<!-- Children Section -->
<h3>Children's Names</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="child_name1">Child 1 Name</label>
<input type="text" id="child_name1" name="child_name1" class="form-control" placeholder="Child 1 Name">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_dob1">Child 1 DOB</label>
<input type="date" id="child_dob1" name="child_dob1" class="form-control">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_name2">Child 2 Name</label>
<input type="text" id="child_name2" name="child_name2" class="form-control" placeholder="Child 2 Name">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_dob2">Child 2 DOB</label>
<input type="date" id="child_dob2" name="child_dob2" class="form-control">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_name3">Child 3 Name</label>
<input type="text" id="child_name3" name="child_name3" class="form-control" placeholder="Child 3 Name">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_dob3">Child 3 DOB</label>
<input type="date" id="child_dob3" name="child_dob3" class="form-control">
</div>
</div>
<!-- Repeat for other children if needed -->
</div>
<!-- Address Section -->
<h3>Address</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="physical_address">Physical Address</label>
<textarea id="physical_address" name="physical_address" class="form-control" placeholder="Enter physical address"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="postal_address">Postal Address</label>
<textarea id="postal_address" name="postal_address" class="form-control" placeholder="Enter postal address"></textarea>
</div>
</div>
</div>
<!-- Interests Section -->
<h3>Interests and Hobbies</h3>
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<textarea id="interests_hobbies" name="interests_hobbies" class="form-control" placeholder="Enter interests and hobbies"></textarea>
</div>
</div>
</div>
<!-- Vehicle Section -->
<h3>Primary Vehicle</h3>
<div class="row mt-35">
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_make">Make</label>
<input type="text" id="vehicle_make" name="vehicle_make" class="form-control" placeholder="Vehicle Make">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_model">Model</label>
<input type="text" id="vehicle_model" name="vehicle_model" class="form-control" placeholder="Vehicle Model">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_year">Year</label>
<input type="text" id="vehicle_year" name="vehicle_year" class="form-control" placeholder="Vehicle Year">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_registration">Registration</label>
<input type="text" id="vehicle_registration" name="vehicle_registration" class="form-control" placeholder="Vehicle Registration">
</div>
</div>
</div>
<h3>Secondary Vehicle</h3>
<div class="row mt-35">
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_make">Make</label>
<input type="text" id="secondary_vehicle_make" name="secondary_vehicle_make" class="form-control" placeholder="Vehicle Make">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_model">Model</label>
<input type="text" id="secondary_vehicle_model" name="secondary_vehicle_model" class="form-control" placeholder="Vehicle Model">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_year">Year</label>
<input type="text" id="secondary_vehicle_year" name="secondary_vehicle_year" class="form-control" placeholder="Vehicle Year">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_registration">Registration</label>
<input type="text" id="secondary_vehicle_registration" name="secondary_vehicle_registration" class="form-control" placeholder="Vehicle Registration">
</div>
</div>
</div>
</div>
<!-- Submit Section -->
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" class="theme-btn style-two" style="width:100%;">Next</button>
<div id="msgSubmit" class="hidden"></div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Form Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,562 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
// Ensure the user is logged in
if (!isset($_SESSION['user_id'])) {
die("User not logged in.");
}
$user_id = $_SESSION['user_id'];
checkMembershipApplication2($user_id);
// Fetch the user's membership details
$sql = "SELECT membership_start_date, membership_end_date, payment_status, payment_amount, payment_id FROM membership_fees WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
// Fetch single record
$membership = $result->fetch_assoc();
// Fetch membership application data using mysqli
$query = "SELECT * FROM membership_application WHERE user_id = ?";
$stmt = $conn->prepare($query);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
$application = $result->fetch_assoc();
// Close statement
$stmt->close();
if (empty($application['id_number'])) {
$_SESSION['message'] = 'Please update your details below to complete your application.';
}
?>
<style>
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin: 10px 0;
}
thead th {
cursor: pointer;
text-align: left;
padding: 10px;
font-weight: bold;
position: relative;
}
thead th::after {
content: '\25B2';
/* Up arrow */
font-size: 0.8em;
position: absolute;
right: 10px;
opacity: 0;
transition: opacity 0.2s;
}
thead th.asc::after {
content: '\25B2';
/* Up arrow */
opacity: 1;
}
thead th.desc::after {
content: '\25BC';
/* Down arrow */
opacity: 1;
}
tbody tr:nth-child(odd) {
background-color: transparent;
}
tbody tr:nth-child(even) {
background-color: rgb(255, 255, 255);
border-radius: 10px;
}
tbody td {
padding: 5px;
}
tbody tr:nth-child(even) td:first-child {
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
tbody tr:nth-child(even) td:last-child {
border-top-right-radius: 10px;
border-bottom-right-radius: 10px;
}
.filter-input {
width: 100%;
padding: 5px;
/* margin-bottom: 20px; */
font-size: 16px;
background-color: rgb(255, 255, 255);
border-radius: 25px;
}
.infobox {
color: #484848;
background: #f9f9f7;
border: 1px solid #d8d8d8;
border-radius: 10px;
margin-top: 15px;
margin-bottom: 15px;
}
.message-box {
text-align: center;
position: relative;
padding: 10px;
padding-right: 35px;
/* Ensures text doesn't overlap with the close button */
}
.close-btn {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
/* Centers vertically */
cursor: pointer;
font-size: 20px;
font-weight: bold;
color: #333;
background: none;
border: none;
}
.close-btn:hover {
color: red;
}
</style>
<!-- Account Settings Area start -->
<section class="account-settings-area py-70 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-12">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<div class="section-title py-20">
<h2>Membership Details</h2>
<div id="responseMessage"></div> <!-- Message display area -->
<?php if (isset($_SESSION['message'])): ?>
<div class="alert alert-success message-box">
<?php echo $_SESSION['message']; ?>
<span class="close-btn" onclick="this.parentElement.style.display='none'">&times;</span>
</div>
<?php unset($_SESSION['message']); ?>
<?php endif; ?>
</div>
<!-- Table Display -->
<div style='padding:10px;'>
<table>
<thead>
<tr>
<th>Start Date</th>
<th>Renewal Date</th>
<th>Indemnity</th>
<th>Amount</th>
<th>Payment Reference</th>
<th>Payment Status</th>
<th>Membership Status</th>
</tr>
</thead>
<tbody>
<?php if ($membership): ?>
<tr>
<td><?php echo htmlspecialchars($membership['membership_start_date']); ?></td>
<td><?php echo htmlspecialchars($membership['membership_end_date']); ?></td>
<?php if (hasAcceptedIndemnity($user_id)) { ?>
<td><a href='view_indemnity.php' class='theme-btn style-two style-three' style='padding: 0px 14px;'><span data-hover='VIEW INDEMNITY'>VIEW INDEMNITY</span></a></td>
<?php } else { ?>
<td><a href='indemnity.php' class='theme-btn style-two style-three' style='padding: 0px 14px;'><span data-hover='SIGN INDEMNITY'>SIGN INDEMNITY</span></a></td>
<?php } ?>
<td><?php echo htmlspecialchars($membership['payment_amount']); ?></td>
<td><?php echo htmlspecialchars($membership['payment_id']); ?></td>
<?php if ($membership['payment_status'] == "PENDING") { ?>
<td><a href='membership_payment' class='theme-btn style-two style-three' style='padding: 0px 14px;'><span data-hover='VIEW PAYMENT INFO'>AWAITING PAYMENT</span></a></td>
<?php } else { ?>
<td><?php echo htmlspecialchars($membership['payment_status']); ?></td>
<?php } ?>
<td><?php echo getUserMemberStatus($user_id) ? 'ACTIVE' : 'INACTIVE'; ?></td>
</tr>
<?php else: ?>
<tr>
<td colspan="6">No membership records found.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php
// Check if membership has expired
$membership_end_date = $membership ? $membership['membership_end_date'] : null;
$today = date('Y-m-d');
if ($membership_end_date && strtotime($today) > strtotime($membership_end_date)) {
echo '
<a href="renew_membership" class="theme-btn style-two bgc-secondary" style="width:100%; margin-top: 20px; background-color: #63ab45; padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">
<span data-hover="Renew Membership">Renew Membership</span>
<i class="fal fa-arrow-right"></i>
</a>';
}
?>
<form id="infoForm" name="registerForm" action="update_application" method="post" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<div id="responseMessage"></div> <!-- Message display area -->
</div>
<!-- Personal Details Section -->
<h3>Main Member</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="first_name">First Name*</label>
<input type="text" id="first_name" name="first_name" class="form-control" value="<?php echo htmlspecialchars($application['first_name'] ?? ''); ?>" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="last_name">Surname*</label>
<input type="text" id="last_name" name="last_name" class="form-control" value="<?php echo htmlspecialchars($application['last_name'] ?? ''); ?>" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="id_number">ID Number / Passport Number*</label>
<input type="text" id="id_number" name="id_number" class="form-control" value="<?php echo htmlspecialchars($application['id_number'] ?? ''); ?>" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="dob">Date of Birth*</label>
<input type="date" id="dob" name="dob" class="form-control" value="<?php echo htmlspecialchars($application['dob'] ?? ''); ?>" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="occupation">Occupation*</label>
<input type="text" id="occupation" name="occupation" class="form-control" value="<?php echo htmlspecialchars($application['occupation'] ?? ''); ?>" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="tel_cell">Cell Phone*</label>
<input type="text" id="tel_cell" name="tel_cell" class="form-control" value="<?php echo htmlspecialchars($application['tel_cell'] ?? ''); ?>" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="email">Email Address*</label>
<input type="email" id="email" name="email" class="form-control" value="<?php echo htmlspecialchars($application['email'] ?? ''); ?>" required>
</div>
</div>
</div>
<!-- Spouse / Partner Details Section -->
<h3>Spouse / Life Partner / Other Details</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="spouse_first_name">First Name</label>
<input type="text" id="spouse_first_name" name="spouse_first_name" class="form-control" value="<?php echo htmlspecialchars($application['spouse_first_name'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="spouse_last_name">Surname</label>
<input type="text" id="spouse_last_name" name="spouse_last_name" class="form-control" value="<?php echo htmlspecialchars($application['spouse_last_name'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="spouse_id_number">ID Number / Passport Number</label>
<input type="text" id="spouse_id_number" name="spouse_id_number" class="form-control" value="<?php echo htmlspecialchars($application['spouse_id_number'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="spouse_dob">Date of Birth</label>
<input type="date" id="spouse_dob" name="spouse_dob" class="form-control" value="<?php echo htmlspecialchars($application['spouse_dob'] ?? ''); ?>">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="spouse_occupation">Occupation</label>
<input type="text" id="spouse_occupation" name="spouse_occupation" class="form-control" value="<?php echo htmlspecialchars($application['spouse_occupation'] ?? ''); ?>">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="spouse_phone_numbers">Cell Phone</label>
<input type="text" id="spouse_tel_cell" name="spouse_tel_cell" class="form-control" value="<?php echo htmlspecialchars($application['spouse_tel_cell'] ?? ''); ?>">
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="spouse_email">Email Address</label>
<input type="email" id="spouse_email" name="spouse_email" class="form-control" value="<?php echo htmlspecialchars($application['spouse_email'] ?? ''); ?>">
</div>
</div>
</div>
<!-- Children Section -->
<h3>Children's Names</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="child_name1">Child 1 Name</label>
<input type="text" id="child_name1" name="child_name1" class="form-control" value="<?php echo htmlspecialchars($application['child_name1'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_dob1">Child 1 DOB</label>
<input type="date" id="child_dob1" name="child_dob1" class="form-control" value="<?php echo htmlspecialchars($application['child_dob1'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_name2">Child 2 Name</label>
<input type="text" id="child_name2" name="child_name2" class="form-control" value="<?php echo htmlspecialchars($application['child_name2'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_dob2">Child 2 DOB</label>
<input type="date" id="child_dob2" name="child_dob2" class="form-control" value="<?php echo htmlspecialchars($application['child_dob2'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_name3">Child 3 Name</label>
<input type="text" id="child_name3" name="child_name3" class="form-control" value="<?php echo htmlspecialchars($application['child_name3'] ?? ''); ?>">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="child_dob3">Child 3 DOB</label>
<input type="date" id="child_dob3" name="child_dob3" class="form-control" value="<?php echo htmlspecialchars($application['child_dob3'] ?? ''); ?>">
</div>
</div>
<!-- Repeat for other children if needed -->
</div>
<!-- Address Section -->
<h3>Address</h3>
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="physical_address">Physical Address</label>
<textarea id="physical_address" name="physical_address" class="form-control" value="<?php echo htmlspecialchars($application['physical_address'] ?? ''); ?>"></textarea>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="postal_address">Postal Address</label>
<textarea id="postal_address" name="postal_address" class="form-control" pvalue="<?php echo htmlspecialchars($application['postal_address'] ?? ''); ?>"></textarea>
</div>
</div>
</div>
<!-- Interests Section -->
<h3>Interests and Hobbies</h3>
<div class="row mt-35">
<div class="col-md-12">
<div class="form-group">
<textarea id="interests_hobbies" name="interests_hobbies" class="form-control" value="<?php echo htmlspecialchars($application['interests_hobbies'] ?? ''); ?>"></textarea>
</div>
</div>
</div>
<!-- Vehicle Section -->
<h3>Primary Vehicle</h3>
<div class="row mt-35">
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_make">Make</label>
<input type="text" id="vehicle_make" name="vehicle_make" class="form-control" value="<?php echo htmlspecialchars($application['vehicle_make'] ?? ''); ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_model">Model</label>
<input type="text" id="vehicle_model" name="vehicle_model" class="form-control" value="<?php echo htmlspecialchars($application['vehicle_model'] ?? ''); ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_year">Year</label>
<input type="text" id="vehicle_year" name="vehicle_year" class="form-control" value="<?php echo htmlspecialchars($application['vehicle_year'] ?? ''); ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="vehicle_registration">Registration</label>
<input type="text" id="vehicle_registration" name="vehicle_registration" class="form-control" value="<?php echo htmlspecialchars($application['vehicle_registration'] ?? ''); ?>">
</div>
</div>
</div>
<h3>Secondary Vehicle</h3>
<div class="row mt-35">
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_make">Make</label>
<input type="text" id="secondary_vehicle_make" name="secondary_vehicle_make" class="form-control" value="<?php echo htmlspecialchars($application['secondary_vehicle_make'] ?? ''); ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_model">Model</label>
<input type="text" id="secondary_vehicle_model" name="secondary_vehicle_model" class="form-control" value="<?php echo htmlspecialchars($application['secondary_vehicle_model'] ?? ''); ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_year">Year</label>
<input type="text" id="secondary_vehicle_year" name="secondary_vehicle_year" class="form-control" value="<?php echo htmlspecialchars($application['secondary_vehicle_year'] ?? ''); ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="secondary_vehicle_registration">Registration</label>
<input type="text" id="secondary_vehicle_registration" name="secondary_vehicle_registration" class="form-control" value="<?php echo htmlspecialchars($application['secondary_vehicle_registration'] ?? ''); ?>">
</div>
</div>
</div>
</div>
<!-- Submit Section -->
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" class="theme-btn style-two" style="width:100%;">Update Info</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- Account Settings Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Clear the responseMessage when the user changes any form input
$('#accountForm input, #changePasswordForm input').on('input', function() {
$('#responseMessage').html(''); // Clear the message
$('#responseMessage2').html(''); // Clear the message
});
// Profile Picture Upload
$('#uploadPictureBtn').click(function() {
$('#profile_picture').click();
});
$('#profile_picture').on('change', function() {
var formData = new FormData();
formData.append('profile_picture', $('#profile_picture')[0].files[0]);
$.ajax({
url: 'upload_profile_picture',
type: 'POST',
data: formData,
contentType: false,
processData: false,
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
// Update the profile picture source with cache-busting query string
// Reload the current page
window.location.reload();
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture.</div>');
}
});
});
// Account Info Update
$('#accountForm').on('submit', function(event) {
event.preventDefault(); // Prevent default form submission
$.ajax({
url: 'update_user',
type: 'POST',
data: $(this).serialize(),
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error updating information.</div>');
}
});
});
// Change Password
$('#changePasswordForm').on('submit', function(event) {
event.preventDefault(); // Prevent default form submission
$.ajax({
url: 'change_password',
type: 'POST',
data: $(this).serialize(),
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
$('#responseMessage2').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage2').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage2').html('<div class="alert alert-danger">Error changing password.</div>');
}
});
});
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,105 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
} else {
header('Location: login.php');
exit(); // Stop further script execution
}
// Initialize variables
$payment_amount = null;
$membership_start_date = null;
$membership_end_date = null;
// Get the user_id from the session
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
if ($user_id) {
// Prepare the SQL query to fetch data
$query = "SELECT payment_amount, membership_start_date, membership_end_date, payment_id
FROM membership_fees
WHERE user_id = ?";
if ($stmt = $conn->prepare($query)) {
// Bind the user_id parameter to the query
$stmt->bind_param("i", $user_id);
// Execute the query
$stmt->execute();
// Bind the results to variables
$stmt->bind_result($payment_amount, $membership_start_date, $membership_end_date, $eft_id);
// Fetch the data
if ($stmt->fetch()) {
// Values are now assigned to $payment_amount, $membership_start_date, and $membership_end_date
} else {
// Handle case where no records are found
$error_message = "No records found for the given user ID.";
}
// Close the statement
$stmt->close();
} else {
// Handle query preparation failure
$error_message = "Query preparation failed: " . $conn->error;
}
} else {
// Handle case where user_id is not found in session
$error_message = "User ID not found in session.";
}
// Get user's email
$sql = "SELECT email FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
die("Prepare failed: " . $conn->error);
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($user_email);
$stmt->fetch();
$stmt->close();
$conn->close();
?><?php
$pageTitle = 'Membership Payment';
$breadcrumbs = [['Home' => 'index.php'], ['Membership' => 'membership.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Contact Form Area start -->
<section class="about-us-area py-100 rpb-90 rel z-1">
<div class="container">
<div class="row">
<div class="col-lg-6">
<div class="section-title mb-25">
<span class="h2 mb-15">New Membership Payment:</span>
<?php echo
'<h5>Membership Start Date: ' . $membership_start_date . '<br>Membership Renewal Date: ' . $membership_end_date . '</h5>'; ?>
</div>
<p>Your invoice has been sent to <b><?php echo htmlspecialchars($user_email); ?></b>. Please upload your proof of payment below.</p>
<h5>Payment Details:</h5>
<p>The Four Wheel Drive Club of Southern Africa<br>FNB<br>Account Number: 58810022334<br>Branch code: 250655<br>Reference: <?php echo htmlspecialchars($eft_id); ?><br>Amount: R <?php echo number_format($payment_amount, 2); ?></p>
<a href="submit_pop" class="theme-btn style-two style-three" style="width:100%;">
<span data-hover="Submit Proof of Payment">Submit Proof of Payment</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
<div class="col-lg-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="about-us-image">
<img src="assets/images/logos/weblogo.png" alt="About">
</div>
</div>
</div>
</div>
</section>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,32 @@
<?php
require_once("env.php");
require_once("session.php");
require_once("connection.php");
require_once("functions.php");
$user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null;
$eft_id = strtoupper("SUBS " . date("Y") . " " . getLastName($user_id));
$status = 'AWAITING PAYMENT';
$description = 'Membership Fees ' . date("Y") . " " . getLastName($user_id);
$payment_amount = 2500; // Assuming a fixed membership fee, adjust as needed
$payment_date = date('Y-m-d');
$membership_start_date = date('Y-01-01');
$membership_end_date = date('Y-12-31');
$stmt = $conn->prepare("UPDATE membership_fees SET payment_amount = ?, payment_date = ?, membership_start_date = ?, membership_end_date = ?, payment_status = 'PENDING', payment_id = ? WHERE user_id = ?");
$stmt->bind_param("dssssi", $payment_amount, $payment_date, $membership_start_date, $membership_end_date, $eft_id, $user_id);
if ($stmt->execute()) {
// Commit the transaction
$conn->commit();
addSubsEFT($eft_id, $user_id, $status, $payment_amount, $description);
header("Location:membership_payment.php");
// Success message
$response = [
'status' => 'success',
'message' => 'Your membership application has been updated successfully!'
];
} else {
throw new Exception("Failed to update membership fee. SQL error: " . $conn->error);
}

46
src/pages/other/404.php Normal file
View File

@@ -0,0 +1,46 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
?>
<!-- 404 Error Area start -->
<section class="error-area pt-70 pb-100 rel z-1">
<div class="container">
<div class="row align-items-center justify-content-between">
<div class="col-xl-5 col-lg-6">
<div class="error-content rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<h1>OOPS! </h1>
<div class="section-title mt-15 mb-25">
<h2>This Page Cant be Found</h2>
</div>
<!-- <p>Best features to include on business landing page are those that quickly convey your value proposition, build trust, and encourage action. Here are six essential features</p> -->
<!-- <form class="newsletter-form mt-40 mb-50" action="#">
<input id="news-email" type="text" placeholder="Search keyword" required>
<button type="submit" class="theme-btn bgc-secondary style-two">
<span data-hover="Search">Search</span>
<i class="fal fa-arrow-right"></i>
</button>
</form>
<div class="keywords">
<a href="blog.html">Travel</a>
<a href="blog.html">Luxury Hotel</a>
<a href="blog.html">Indonesia</a>
<a href="blog.html">Sea Beach</a>
<a href="blog.html">Camping</a>
<a href="blog.html">Hiking</a>
<a href="blog.html">Fishing</a>
</div> -->
</div>
</div>
<div class="col-xl-5 col-lg-6">
<div class="error-images" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/404/lost.jpg" alt="404 Error">
</div>
</div>
</div>
</div>
</section>
<!-- 404 Error Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

284
src/pages/other/about.php Normal file
View File

@@ -0,0 +1,284 @@
<?php
$headerStyle = 'light';
// Determine the correct path to header.php based on file location
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style>
.gallery-slider-active {
display: flex;
flex-wrap: wrap;
gap: 16px;
/* spacing between images */
justify-content: center;
}
.gallery-three-item {
width: 520px;
height: 300px;
overflow: hidden;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
background: #f9f9f9;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.gallery-three-item .image {
flex-grow: 1;
width: 100%;
height: 100%;
}
.gallery-three-item img {
width: 100%;
height: 100%;
object-fit: cover;
/* ensures aspect ratio while filling container */
display: block;
}
</style>
<?php
$pageTitle = 'About';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Benefit Area start -->
<section class="benefit-area mt-100 rel z-1">
<div class="container">
<div class="row align-items-center justify-content-between">
<div class="col-xl-5 col-lg-6">
<div class="mobile-app-content rmb-55" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title counter-text-wrap mb-40">
<h2>Welcome to the Four Wheel Drive Club of Southern Africa!</h2>
</div>
<p style="max-width: 600px; margin: 0 auto;">
We're a family-friendly outdoor adventure club passionate about exploring the great outdoors through off-road driving, camping, overlanding, cross-border trips, day trips, and unforgettable events. Whether you're new to 4x4 adventures or a seasoned explorer, our community is all about camaraderie, responsible adventure, and creating lasting memories—on and off the road.
</p>
<ul class="list-style-two mt-35 mb-30">
<li>Overlanding</li>
<li>Camping</li>
<li>Day Trips</li>
<li>4x4 Driver Training</li>
<li>Family Events</li>
<li>Monthly Open Days</li>
<li>Guest Speakers</li>
<li>4x4 Driving Track</li>
</ul>
<!-- <a href="about.html" class="theme-btn style-two">
<span data-hover="Explore Guides">Explore Guides</span>
<i class="fal fa-arrow-right"></i>
</a> -->
</div>
</div>
<div class="col-lg-6">
<div class="benefit-image-part style-two">
<div class="image-one" data-aos="fade-down" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/benefit/benefit1.png" alt="Benefit">
</div>
<div class="image-two" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<img src="assets/images/benefit/benefit2.png" alt="Benefit">
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Benefit Area end -->
<!-- Hotel Area start -->
<section class="hotel-area bgc-black py-100 rel z-1">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-title text-white text-center counter-text-wrap mb-70" data-aos="fade-up"
data-aos-duration="1500" data-aos-offset="50">
<h2>BASE4 Open Days</h2>
<p style="max-width: 60%; margin: auto;">Whether you're a member or just curious, everyone's welcome at our monthly open events. Come camp with us, enjoy guest speakers, take your rig for a spin on the 4x4 track, or just relax by the swimming pool. Saturdays Open Day includes breakfast and lunch for sale, plus braai fires ready to go—just bring your tongs! Its the perfect way to experience the spirit of the club and connect with fellow adventurers. </p>
</div>
</div>
</div>
<div class="gallery-slider-active">
<?php
$folder = $rootPath . '/assets/images/opendays/';
$images = glob($folder . '*.{jpg,jpeg,png,gif}', GLOB_BRACE);
// Convert absolute paths to web-relative paths
$images = array_map(function($path) use ($rootPath) {
return str_replace($rootPath, '', $path);
}, $images);
// Shuffle and pick first 5
shuffle($images);
$selected = array_slice($images, 0, 10);
foreach ($selected as $image) {
echo '<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="' . $image . '" alt="Gallery">
</div>
</div>';
}
?>
</div>
</div>
<!-- <div class="hotel-more-btn text-center mt-40">
<a href="destination2.html" class="theme-btn style-four">
<span data-hover="Explore More Hotel">Explore More Hotel</span>
<i class="fal fa-arrow-right"></i>
</a>
</div> -->
</div>
</section>
<!-- Hotel Area end -->
<!-- Features Area start -->
<section class="features-area pt-100 pb-45 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-xl-6">
<div class="features-content-part mb-55" data-aos="fade-left" data-aos-duration="1500"
data-aos-offset="50">
<div class="section-title mb-20">
<h2>Want to get involved?<b>JOIN THE COMMITTEE!</b></h2>
<p>Want to be more involved in the adventure? Join our committee and help shape the future of the club! Whether its planning epic trips, organizing fun events, or assisting with training, your energy and ideas make all the difference. The club runs on the passion of its members—get stuck in, meet awesome people, and be part of what makes it all happen!</p>
<div class="image">
<img style="border-radius:10px;" src="assets/images/memories/40.jpg" alt="Hotel">
</div>
</div>
</div>
</div>
<div class="col-xl-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="row pb-25">
<div class="section-title text-center counter-text-wrap mb-70" data-aos="fade-up"
data-aos-duration="1500" data-aos-offset="50">
<h2>4WDCSA Committee and Other Office Bearers</h2>
<div>
<h3>Committee</h3>
<li>Chairman - John Runciman</li>
<li>National Liaison - Peter Hutchison</li>
<li>Treasurer - Doug Timm</li>
<li>Outings - John Runciman</li>
<li>Events - Noelene Runciman</li>
<li>Driver Training - John Runciman</li>
<li>Digital Media - Christopher Pinto</li>
</div>
<div class="pt-30 pb-20">
<h3>Administration</h3>
<li>Secretary - Jacqui Boshoff</li>
</div>
<p style="font-size:0.8rem;">
All portfolio holders/committee members of the 4WDCSA are volunteers and are not paid for their services.<br>The secretary is paid for administrative duties only.</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Area end -->
<!-- Hotel Area start -->
<section class="hotel-area bgc-black py-100 rel z-1">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-title text-white text-center counter-text-wrap mb-70" data-aos="fade-up"
data-aos-duration="1500" data-aos-offset="50">
<h2>4x4 Memories</h2>
</div>
</div>
</div>
<div class="gallery-slider-active"><?php
$folder = $rootPath . '/assets/images/memories/';
$images = glob($folder . '*.{jpg,jpeg,png,gif}', GLOB_BRACE);
// Convert absolute paths to web-relative paths
$images = array_map(function($path) use ($rootPath) {
return str_replace($rootPath, '', $path);
}, $images);
// Shuffle and pick first 5
shuffle($images);
$selected = array_slice($images, 0, 20);
foreach ($selected as $image) {
echo '<div class="gallery-three-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="' . $image . '" alt="Gallery">
</div>
</div>';
}
?>
</div>
</div>
<!-- <div class="hotel-more-btn text-center mt-40">
<a href="destination2.html" class="theme-btn style-four">
<span data-hover="Explore More Hotel">Explore More Hotel</span>
<i class="fal fa-arrow-right"></i>
</a>
</div> -->
</div>
</section>
<!-- Hotel Area end -->
<!-- CTA Area start -->
<section class="cta-area pt-100 rel z-1">
<div class="container-fluid">
<div class="row">
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-duration="1500" data-aos-offset="50">
<div class="cta-item" style="background-image: url(assets/images/trips/1_01.jpg);">
<span class="category">Extended Trips</span>
<h2>Come and Explore Africa and beyond</h2>
<a href="<?= url('trips') ?>" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Tours">Explore Trips</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="cta-item" style="background-image: url(assets/images/courses/driver_training.png);">
<span class="category">Driver Training</span>
<h2>Level up your 4x4 Driving Skills</h2>
<a href="<?= url('driver_training') ?>" class="theme-btn style-two">
<span data-hover="Explore Tours">Explore Training</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
<div class="cta-item" style="background-image: url(assets/images/base4/camping.jpg);">
<span class="category">Events</span>
<h2>See whats cooking at BASE4!</h2>
<a href="<?= url('events') ?>" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Tours">Explore Events</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
</div>
</div>
</section>
<!-- CTA Area end -->
<!-- Blog Area start -->
<section class="blog-area pt-70 rel z-1">
<div class="container">
<div class="row justify-content-center">
</div>
</div>
</section>
<!-- Blog Area end -->
<?php include_once($rootPath . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,249 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
// Assuming you have the user ID stored in the session
$user_id = $_SESSION['user_id'];
// Fetch user data from the database
$sql = "SELECT * FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
?>
<style>
.profile-picture:hover .edit-icon {
display: block;
}
.profile-picture {
position: relative;
width: 150px;
height: 150px;
margin: 0 auto;
}
.profile-pic-display {
width: 100%;
height: 100%;
border-radius: 50%;
object-fit: cover;
}
.edit-icon {
display: none;
position: absolute;
width: 100%;
height: 100%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.5);
border: none;
border-radius: 50%;
cursor: pointer;
}
.edit-icon i {
color: white;
}
</style>
<!-- Account Settings Area start -->
<section class="account-settings-area py-70 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-12">
<div class="comment-form bgc-lighter z-1 rel mb-30 rmb-55">
<form id="accountForm" name="accountForm" method="post" action="update_user">
<div class="section-title py-20">
<h2>Account Settings</h2>
<div id="responseMessage"></div> <!-- Message display area -->
</div>
<!-- Display Profile Picture -->
<div class="profile-picture" style="position: relative; width: 150px; height: 150px; margin: 0 auto;">
<img id="profile-pic" src="<?php echo $user['profile_pic']; ?>?v=<?php echo time(); ?>" alt="Profile Picture" class="profile-pic-display"
style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;">
<button type="button" id="uploadPictureBtn" class="edit-icon">
<i class="fas fa-pencil-alt"></i>
</button>
<input type="file" id="profile_picture" name="profile_picture" accept="image/*" style="display:none;">
</div>
<!-- Form Fields -->
<div class="row mt-35">
<div class="col-md-6">
<div class="form-group">
<label for="first_name">First Name</label>
<input type="text" id="first_name" name="first_name" class="form-control" value="<?php echo $user['first_name']; ?>" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="last_name">Last Name</label>
<input type="text" id="last_name" name="last_name" class="form-control" value="<?php echo $user['last_name']; ?>" required>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="phone_number">Phone Number</label>
<input type="text" id="phone_number" name="phone_number" class="form-control" value="<?php echo $user['phone_number']; ?>" required>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" id="email" name="email" class="form-control" value="<?php echo $user['email']; ?>" required>
</div>
</div>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" class="theme-btn style-two" style="width:100%;">Update Info</button>
</div>
</div>
</div>
</form>
<?php if (getUserType($user_id) !== 'google'){?>
<!-- Change Password Form -->
<form id="changePasswordForm" name="changePasswordForm" action="change_password" method="post">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="col-md-12 mt-20">
<h4>Change Password</h4>
<div id="responseMessage2"></div> <!-- Message display area -->
<div class="form-group">
<label for="current_password">Current Password</label>
<input type="password" id="current_password" name="current_password" class="form-control" required>
</div>
<div class="form-group">
<label for="new_password">New Password</label>
<input type="password" id="new_password" name="new_password" class="form-control" required>
</div>
<div class="form-group">
<label for="confirm_password">Confirm New Password</label>
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
</div>
<div class="form-group mb-0">
<button type="submit" class="theme-btn style-two" style="width:100%;">Change Password</button>
</div>
</div>
</form>
<?php }?>
</div>
</div>
</div>
</div>
</section>
<!-- Account Settings Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
$(document).ready(function() {
// Clear the responseMessage when the user changes any form input
$('#accountForm input, #changePasswordForm input').on('input', function() {
$('#responseMessage').html(''); // Clear the message
$('#responseMessage2').html(''); // Clear the message
});
// Profile Picture Upload
$('#uploadPictureBtn').click(function() {
$('#profile_picture').click();
});
$('#profile_picture').on('change', function() {
var formData = new FormData();
formData.append('profile_picture', $('#profile_picture')[0].files[0]);
$.ajax({
url: 'upload_profile_picture',
type: 'POST',
data: formData,
contentType: false,
processData: false,
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
// Update the profile picture source with cache-busting query string
// Reload the current page
window.location.reload();
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture.</div>');
}
});
});
// Account Info Update
$('#accountForm').on('submit', function(event) {
event.preventDefault(); // Prevent default form submission
$.ajax({
url: 'update_user',
type: 'POST',
data: $(this).serialize(),
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
$('#responseMessage').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error updating information.</div>');
}
});
});
// Change Password
$('#changePasswordForm').on('submit', function(event) {
event.preventDefault(); // Prevent default form submission
$.ajax({
url: 'change_password',
type: 'POST',
data: $(this).serialize(),
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
$('#responseMessage2').html('<div class="alert alert-success">' + response.message + '</div>');
} else {
$('#responseMessage2').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage2').html('<div class="alert alert-danger">Error changing password.</div>');
}
});
});
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,29 @@
<div style="padding-left:15px; padding-right:15px;" id="advertisingCarousel" class="carousel slide" data-bs-ride="carousel" data-bs-interval="5000">
<div style="border-radius:20px;" class="carousel-inner">
<?php
$dir = 'assets/images/advertising/';
$images = glob($dir . '*.{jpg,jpeg,png,gif,webp}', GLOB_BRACE);
foreach ($images as $index => $img) {
$active = $index === 0 ? 'active' : '';
echo "<div class='carousel-item $active'>
<img src='$img' class='d-block w-100' style='height:394px; object-fit:cover;' alt='Ad $index'>
</div>";
}
?>
<a class="carousel-control-prev" href="#advertisingCarousel" role="button" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</a>
<a class="carousel-control-next" href="#advertisingCarousel" role="button" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</a>
<div class="carousel-indicators">
<?php foreach ($images as $i => $_): ?>
<button type="button" data-bs-target="#advertisingCarousel" data-bs-slide-to="<?= $i ?>" class="<?= $i === 0 ? 'active' : '' ?>" aria-current="<?= $i === 0 ? 'true' : 'false' ?>" aria-label="Slide <?= $i + 1 ?>"></button>
<?php endforeach; ?>
</div>
</div>
</div>

View File

@@ -0,0 +1,133 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
} else {
header('Location: login.php');
exit(); // Stop further script execution
}
?>
<style>
h2 {
text-align: center;
margin-bottom: 20px;
}
.indemnitycontainer {
max-width: 800px;
margin: 0 auto;
}
.indemnity-text {
text-align: justify;
margin-bottom: 20px;
}
.signature-container {
margin-top: 30px;
margin-bottom: 100px;
text-align: center;
}
#signature-pad {
border: 1px solid black;
}
</style>
<?php
$pageTitle = 'Indemnity';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Page Banner End -->
<div class="indemnitycontainer pt-20">
<!-- <h2>Indemnity and Waiver</h2> -->
<div class="indemnity-text">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div class="signature-container">
<div style="text-align: center;" id="responseMessage"></div> <!-- Message display area -->
<p><strong>Signature</strong></p>
<canvas id="signature-pad" width=400 height=200></canvas><br>
<button type="button" class="theme-btn style-two" id="save">ACCEPT INDEMNITY</button>
<!-- <div class="pt-20" style="text-align: center;">You will be redirected to Payfast for payment.</div> -->
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.0.0/dist/signature_pad.umd.min.js"></script>
<script>
var canvas = document.getElementById('signature-pad');
var signaturePad = new SignaturePad(canvas);
document.getElementById('save').addEventListener('click', function() {
if (signaturePad.isEmpty()) {
alert("Please provide a signature.");
} else {
var dataUrl = signaturePad.toDataURL(); // Get signature as base64 image
$.ajax({
url: 'process_signature',
type: 'POST',
data: {
signature: dataUrl // Send the base64 signature image
},
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
// Check if the user has paid
if (response.paymentStatus === 'PAID') {
// Redirect to membership_details.php if paid
setTimeout(function() {
window.location.href = 'membership_details.php';
}, 2000); // 2-second delay before redirecting
} else {
// Redirect to membership_payment.php if not paid
setTimeout(function() {
window.location.href = 'membership_payment.php';
}, 2000); // 2-second delay before redirecting
}
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error uploading profile picture.</div>');
}
});
}
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,385 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession();
// SQL query to fetch dates for bush mechanics
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ? AND date >= CURDATE()");
$course_type = 'bush_mechanics';
$stmt->bind_param("s", $course_type);
$stmt->execute();
$result = $stmt->get_result();
$page_id = 'bush_mechanics';
?>
<style>
.form-group {
margin-bottom: 15px;
}
select {
width: 100%;
padding: 8px;
font-size: 16px;
}
</style><?php
$pageTitle = 'Bush Mechanics';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Product Details Start -->
<section class="product-details pt-100">
<div class="container">
<div class="row">
<div class="col-lg-6">
<div class="product-details-images rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-content preview-images">
<div class="tab-pane fade preview-item active show" id="preview1">
<img src="assets/images/drivertraining/bm01.jpg" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview2">
<img src="assets/images/drivertraining/bm02.jpg" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview3">
<img src="assets/images/drivertraining/bm03.jpg" alt="Perview">
</div>
</div>
<div class="nav thumb-images rmb-20">
<a href="#preview1" data-bs-toggle="tab" class="thumb-item active show">
<img src="assets/images/drivertraining/bm01.jpg" alt="Thumb">
</a>
<a href="#preview2" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/drivertraining/bm02.jpg" alt="Thumb">
</a>
<a href="#preview3" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/drivertraining/bm03.jpg" alt="Thumb">
</a>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="product-details-content" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>Bush Mechanics</h2>
</div>
<!-- <div class="ratting mb-15">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
<span>(5.8k+ reviews)</span>
</div> -->
<span class="price mb-5">R <?= getPrice('bush_mechanics', 'member');?>/member</span>
<span class="price mb-25">R <?= getPrice('bush_mechanics', 'nonmember');?>/non-members</span>
<p> This Bush Mechanics Course is tailored to help you develop the essential skills for managing vehicle repairs and maintenance in remote, off-grid locations. Learn practical techniques for diagnosing and fixing mechanical issues using limited resources, from tire repairs to engine troubleshooting. The course covers the use of basic tools, improvising solutions in the field, and ensuring your vehicle remains operational even in the most challenging environments. Perfect for off-road adventurers, 4x4 owners, and those who want to be prepared for any mechanical situation while exploring remote areas.</p>
<hr class="mt-40">
<div class="blog-sidebar tour-sidebar">
<div class="widget widget-booking" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="process_course_booking" method="POST">
<ul class="tickets clearfix">
<li>
Select Date
<select name="course_id" id="course_id" required>
<?php
if ($result->num_rows > 0) {
// Output each course as an option
while ($row = $result->fetch_assoc()) {
$course_id = htmlspecialchars($row['course_id']); // Escape output for security
$date = htmlspecialchars($row['date']); // Escape output for security
echo "<option value='$course_id'>$date</option>";
}
} else {
echo "<option value='' disabled>No dates available</option>";
}
?>
</select>
</li>
<?php
if ($is_member || $pending_member) {
echo '
<li>
Additional Members <span class="price"></span>
<select name="members" id="members">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
';
} ?>
<li>
Additional Non-Members <span class="price"></span>
<select name="non-members" id="non-members">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
</ul>
<hr class="mb-25">
<h6>Total: <span id="booking_total" class="price">-</span></h6>
<div style="margin: 20px 0;">
<div id="indemnityBox" style="border: 1px solid #ccc; padding: 10px; height: 150px; overflow-y: scroll; background: #f9f9f9; font-size: 12px;">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div style="margin-top: 10px;">
<input type="checkbox" id="agreeCheckbox" name="agree" disabled required>
<label for="agreeCheckbox" id="agreeLabel" style="color: #888;">I have read and agree to the indemnity terms</label>
</div>
</div>
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<?php
$button_text = "Book Now";
$button_disabled = "";
if (!$result || $result->num_rows == 0) {
$button_text = "No booking dates available";
$button_disabled = "disabled";
}
?>
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5" <?php echo $button_disabled; ?>>
<span data-hover="<?php echo $button_text; ?>"><?php echo $button_text; ?></span>
<i class="fal fa-arrow-right"></i>
</button>
<div class="text-center">
<a href="contact">Need some help?</a>
</div>
</form>
</div>
</div>
<!-- <hr class="mb-45"> -->
<!-- <a href="#" class="wishlist"><i class="far fa-heart"></i> Add to Wishlist</a> -->
</div>
</div>
</div>
<ul class="nav nav product-tab mt-70 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li><a href="#details" data-bs-toggle="tab" class="active show">Course Overview<i class="far fa-arrow-right"></i></a></li>
<li><a href="#information" data-bs-toggle="tab">What to Expect<i class="far fa-arrow-right"></i></a></li>
<li><a href="#reviews" data-bs-toggle="tab"> Reviews <i class="far fa-arrow-right"></i></a></li>
</ul>
<div class="tab-content" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-pane fade active show" id="details">
<p>This Bush Mechanics Course is designed to provide participants with practical, hands-on skills and knowledge for conducting essential repairs and maintenance in remote and off-road environments. Participants will learn how to assess mechanical issues and apply bush-friendly repair techniques, ensuring they can keep their 4x4 running smoothly in the field. The course covers a wide range of topics, from tire repairs and fixing fuel systems to electrical troubleshooting and engine repairs, all using minimal tools and available resources.</p>
<p>Emphasis is placed on the use of basic tools, improvising with available materials, and maintaining the vehicles functionality in harsh conditions. The course also prioritizes safety, teaching participants how to perform repairs while minimizing risk and ensuring they can safely handle mechanical breakdowns during off-road adventures. With a focus on resourcefulness and problem-solving, this course equips off-road enthusiasts and 4x4 owners with the confidence to tackle mechanical challenges and keep their vehicles in top shape while exploring remote locations.</p>
<div class="row gap-50 pt-25 pb-20 align-items-center">
<div class="col-lg-7 pt-15">
<h5>What this course includes</h5>
<ul class="list-style-two mt-25">
<li>Bush Mechanics Manual.</li>
<li>Theory session and discussion.</li>
<li>Spend the afternoon practicing common bush mechanics techniques.</li>
</ul>
</div>
<div class="col-lg-5">
<div class="image rmt-45">
<img src="assets/images/drivertraining/bm04.jpg" alt="Product Details">
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="information">
<!-- <p>Circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses</p> -->
<ul class="list-style-two my-35">
<li>Coffee and Welcome: Kick off the day with a warm coffee, meet your instructors, and receive an overview of the course schedule</li>
<li>Theory Session: Dive into the key principles of off-road driving, including vehicle mechanics, terrain navigation, recovery methods, and safety protocols.</li>
<li>Practical Demonstrations: Watch live demonstrations covering vital techniques like gear selection, adjusting tire pressure, and setting up recovery equipment.</li>
<li>Lunch Break: Enjoy a packed lunch or bring something to braai. Fires will be provided for an authentic outdoor experience.</li>
<li>Practical Bush Mechanics Techniques: Learn hands-on techniques like rebeading a tire, fixing punctures, and performing basic vehicle repairs in the field.</li>
<li>Debrief and Certificates: Conclude the day with a review of your progress, feedback from the instructors, and certificates of completion for your off-road training.</li>
</ul>
</div>
<div class="tab-pane fade mb-20" id="reviews">
<?php include_once('review_box.php'); ?>
</div>
</div>
</div>
</section>
<!-- Product Details End -->
<!-- Shop Details Area start -->
<!-- <section class="shop-details-page pt-80 pb-100 rel z-1">
<div class="container">
<div class="section-title text-center mb-40">
<h2>Other Courses</h2>
</div>
<div class="product-slider">
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/shop/product1.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Airport Travel Suitcases</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="50">
<div class="image">
<img src="assets/images/shop/product2.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Travel Great blue hat</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="100">
<div class="image">
<img src="assets/images/shop/product3.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Waistband and Mesh Fashion</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="150">
<div class="image">
<img src="assets/images/shop/product4.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Sandals for Casual Techies</a></h6>
<span class="price">$188.00</span>
</div>
</div>
<div class="product-item" data-aos="flip-left" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/shop/product5.png" alt="Product">
</div>
<div class="content">
<div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div>
<h6><a href="product-details.html">Children With Jute Soles</a></h6>
<span class="price">$188.00</span>
</div>
</div>
</div>
</div>
</section> -->
<!-- Shop Details Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
const indemnityBox = document.getElementById('indemnityBox');
const agreeCheckbox = document.getElementById('agreeCheckbox');
const bookingForm = document.querySelector('form');
indemnityBox.addEventListener('scroll', function () {
const scrollTop = indemnityBox.scrollTop;
const scrollHeight = indemnityBox.scrollHeight;
const offsetHeight = indemnityBox.offsetHeight;
// Enable checkbox when scrolled to bottom
if (scrollTop + offsetHeight >= scrollHeight - 1) {
agreeCheckbox.disabled = false;
document.getElementById('agreeLabel').style.color = "#000"; // optional: make label active
}
});
bookingForm.addEventListener('submit', function (e) {
if (agreeCheckbox.disabled || !agreeCheckbox.checked) {
alert('Please read and agree to the indemnity terms before booking.');
e.preventDefault(); // stop form submission
}
});
</script>
<script>
$(document).ready(function() {
// Function to calculate booking total
function calculateTotal() {
// Get selected values from the form
var members = parseInt($('#members').val()) || 0; // Default to 1 vehicle if not selected
var nonmembers = parseInt($('#non-members').val()) || 0; // Default to 1 adult if not selected
// Fetch PHP variables
var isMember = <?php echo $is_member ? 'true' : 'false'; ?>;
var pendingMember = <?php echo $pending_member ? 'true' : 'false'; ?>;
var cost_members = <?= getPrice('bush_mechanics', 'member');?>;
var cost_nonmembers = <?= getPrice('bush_mechanics', 'nonmember');?>;
// Calculate the total cost based on membership
var total = 0;
// Calculate cost for members
if (isMember || pendingMember) {
total = (cost_members) + (members * cost_members) + (nonmembers * cost_nonmembers);
} else {
// Calculate cost for non-members
total = (cost_nonmembers) + (members * cost_members) + (nonmembers * cost_nonmembers);
}
// Update total price in the DOM
$('#booking_total').text('R ' + total.toFixed(2));
}
// Event listeners to trigger recalculation when any form field changes
$('#members, #non-members').on('change', function() {
calculateTotal();
});
// Initial calculation on page load
calculateTotal();
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,159 @@
<?php
if (!isset($page_id)) {
die("Page ID not set for comment system.");
}
$conn = openDatabaseConnection();
// Handle comment post
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_comment'])) {
// Validate CSRF token
if (!isset($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
http_response_code(403);
die('Security token validation failed.');
}
$comment = trim($_POST['comment'] ?? '');
if (!empty($comment)) {
$stmt = $conn->prepare("INSERT INTO comments (page_id, user_id, comment) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $page_id, $user_id, $comment);
if ($stmt->execute()) {
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
}
}
// Fetch comments
$stmt = $conn->prepare("SELECT user_id, comment, created_at FROM comments WHERE page_id = ? ORDER BY created_at DESC");
$stmt->bind_param("s", $page_id);
$stmt->execute();
$result = $stmt->get_result();
?>
<div>
<h5>Comments</h5>
<div class="comments">
<?php while ($row = $result->fetch_assoc()): ?>
<div class="comment-body" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div>
<img class="profile-pic" src="<?= getProfilePic($user_id); ?>" alt="Author">
</div>
<div class="">
<h6><?= getFullName($row['user_id']); ?></h6>
<?php
if (getUserMemberStatus($row['user_id'])){
echo '<div class="badge badge-primary badge-pill">MEMBER</div>';
}
?>
<em><?= $row['created_at'] ?></em>
<!-- <div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div> -->
<p><?= nl2br(htmlspecialchars($row['comment'])) ?></p>
<!-- <a class="read-more" href="#">Reply <i class="far fa-angle-right"></i></a> -->
</div>
</div>
<?php endwhile; ?>
</form>
<!-- <h5>Add A Comment</h5> -->
<form method="POST" id="comment-form" class="comment-form bgc-lighter z-1 rel mt-30" name="review-form" action="" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="row gap-20">
<div class="col-md-12">
<div class="form-group">
<textarea name="comment" id="comment" class="form-control" rows="5" placeholder="Add comment..." required></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" name="submit_comment" class="theme-btn bgc-secondary style-two">
<span data-hover="Submit reviews">Add comment</span>
<i class="fal fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</form>
</div>
<style>
.comment-box {
border: 1px solid #ccc;
padding: 10px;
max-width: 600px;
}
.comment-box form input,
.comment-box form textarea {
width: 100%;
margin-bottom: 8px;
}
.comments-list {
margin-top: 20px;
}
.comment {
border-top: 1px solid #eee;
padding-top: 10px;
margin-top: 10px;
}
.profile-pic {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
/* Ensures the image fits without distortion */
}
.badge {
display: inline-block;
padding: 0.4em 0.8em;
font-size: 0.875rem;
font-weight: 600;
color: white;
border-radius: 0.375em;
margin-right: 0.5em;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-primary {
background-color: #e90000;
}
.badge-success {
background-color: #28a745;
}
.badge-warning {
background-color: #ffc107;
color: #212529;
}
.badge-danger {
background-color: #dc3545;
}
.badge-info {
background-color: #17a2b8;
}
.badge-pill {
border-radius: 999px;
}
</style>

113
src/pages/other/contact.php Normal file
View File

@@ -0,0 +1,113 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
?>
<style>
.image {
width: 400px;
/* Set your desired width */
height: 320px;
/* Set your desired height */
overflow: hidden;
/* Hide any overflow */
display: block;
/* Ensure proper block behavior */
}
.image img {
width: 100%;
/* Image scales to fill the container */
height: 100%;
/* Image scales to fill the container */
object-fit: cover;
/* Fills the container while maintaining aspect ratio */
object-position: top;
/* Aligns the top of the image with the top of the container */
display: block;
/* Prevents inline whitespace issues */
}
</style><?php
$pageTitle = 'Contact Us';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Contact Info Area start -->
<section class="contact-info-area pt-100 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-4">
<div class="contact-info-content mb-30 rmb-55" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title mb-30">
<h2>For any queries, please don't hesitate to contact us:</h2>
</div>
<!-- <p>Our dedicated support team is always ready to assist you with any questions or issues, offering prompt and personalized solutions to meet your needs.</p> -->
<!-- <div class="features-team-box mt-40">
<h6>85+ Expert Team member</h6>
<div class="feature-authors">
<img src="assets/images/features/feature-author1.jpg" alt="Author">
<img src="assets/images/features/feature-author2.jpg" alt="Author">
<img src="assets/images/features/feature-author3.jpg" alt="Author">
<img src="assets/images/features/feature-author4.jpg" alt="Author">
<img src="assets/images/features/feature-author5.jpg" alt="Author">
<img src="assets/images/features/feature-author6.jpg" alt="Author">
<img src="assets/images/features/feature-author7.jpg" alt="Author">
<span>+</span>
</div>
</div> -->
</div>
</div>
<div class="col-lg-8">
<div class="row">
<div class="col-md-6">
<div class="contact-info-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="50">
<div class="icon"><i class="fas fa-envelope"></i></div>
<div class="content">
<h5>Need Help & Support</h5>
<div class="text"><i class="far fa-envelope"></i> <a href="mailto:info@4wdcsa.co.za">info@4wdcsa.co.za</a></div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="contact-info-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="100">
<div class="icon"><i class="fas fa-phone"></i></div>
<div class="content">
<h5>Need Anything Urgent</h5>
<div class="text"><i class="far fa-phone"></i> <a href="callto:+27 079 065 2795">+2779 065 2795</a></div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="contact-info-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50" data-aos-delay="50">
<div class="icon"><i class="fas fa-map-marker-alt"></i></div>
<div class="content">
<h5>BASE 4</h5>
<div class="text"><i class="fal fa-map-marker-alt"></i> Plot 50, Gemstone Rd, Doornrandje, Centurion, 0157</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Info Area end -->
<!-- Contact Form Area start -->
<!-- -->
<!-- Contact Form Area end -->
<!-- Contact Map Start -->
<div class="contact-map">
<iframe src="https://www.google.com/maps/embed?pb=!1m14!1m12!1m3!1d818.9371187805272!2d28.000391592597513!3d-25.864399472588936!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!5e1!3m2!1sen!2sza!4v1744639736430!5m2!1sen!2sza" style="border:0; width: 100%;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
</div>
<!-- Contact Map End -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,133 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
// Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
} else {
header('Location: login.php');
exit(); // Stop further script execution
}
?>
<style>
h2 {
text-align: center;
margin-bottom: 20px;
}
.indemnitycontainer {
max-width: 800px;
margin: 0 auto;
}
.indemnity-text {
text-align: justify;
margin-bottom: 20px;
}
.signature-container {
margin-top: 30px;
margin-bottom: 100px;
text-align: center;
}
#signature-pad {
border: 1px solid black;
}
</style>
<?php
$pageTitle = 'Indemnity';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Page Banner End -->
<div class="indemnitycontainer pt-20">
<!-- <h2>Indemnity and Waiver</h2> -->
<div class="indemnity-text">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div class="signature-container">
<div style="text-align: center;" id="responseMessage"></div> <!-- Message display area -->
<p><strong>Signature</strong></p>
<canvas id="signature-pad" width=400 height=200></canvas><br>
<button type="button" class="theme-btn style-two" id="save">ACCEPT INDEMNITY</button>
<!-- <div class="pt-20" style="text-align: center;">You will be redirected to Payfast for payment.</div> -->
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.0.0/dist/signature_pad.umd.min.js"></script>
<script>
var canvas = document.getElementById('signature-pad');
var signaturePad = new SignaturePad(canvas);
document.getElementById('save').addEventListener('click', function() {
if (signaturePad.isEmpty()) {
alert("Please provide a signature.");
} else {
var dataUrl = signaturePad.toDataURL(); // Get signature as base64 image
$.ajax({
url: 'process_signature',
type: 'POST',
data: {
signature: dataUrl // Send the base64 signature image
},
success: function(response) {
// Parse response if needed
if (typeof response === "string") {
response = JSON.parse(response);
}
if (response.status === 'success') {
// Check if the user has paid
if (response.paymentStatus === 'PAID') {
// Redirect to membership_details.php if paid
setTimeout(function() {
window.location.href = 'membership_details.php';
}, 2000); // 2-second delay before redirecting
} else {
// Redirect to membership_payment.php if not paid
setTimeout(function() {
window.location.href = 'membership_payment.php';
}, 2000); // 2-second delay before redirecting
}
} else {
$('#responseMessage').html('<div class="alert alert-danger">' + response.message + '</div>');
}
},
error: function() {
$('#responseMessage').html('<div class="alert alert-danger">Error uploading signature.</div>');
}
});
}
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,20 @@
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>

804
src/pages/other/index2.php Normal file
View File

@@ -0,0 +1,804 @@
<?php
$headerStyle = 'dark';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
$indemnityPending = false;
if (isset($_SESSION['user_id'])) {
$userId = $_SESSION['user_id'];
$stmt = $conn->prepare("SELECT user_id FROM membership_application WHERE user_id = ? AND accept_indemnity = 0 LIMIT 1");
$stmt->bind_param("i", $userId);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows > 0) {
$indemnityPending = true;
}
$stmt->close();
}
?>
<style>
.countdown-container {
width: 100%;
/* background: #111; */
text-align: center;
padding: 40px 10px;
/* font-family: Arial, sans-serif; */
}
.countdown-container h1 {
font-size: 3rem;
line-height: 1.5;
}
@media (min-width: 768px) {
.countdown-container h1 {
font-size: 3rem;
}
}
</style>
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="hero-area bgc-black pt-200 rpt-120 rel z-2">
<div style="padding-bottom:30px;" class="container-fluid">
<div style="text-align: center; position: relative; border-radius: 20px; overflow: hidden; background: linear-gradient(rgba(28, 35, 31, 1), rgba(28, 35, 31, 0.5)), url('<?php echo $randomBanner; ?>'); background-size: cover; background-position: center;">
<div style="padding-top: 50px; padding-bottom: 50px;">
<img style="width: 250px; margin-bottom: 20px;" src="assets/images/logos/weblogo2.png" alt="Logo">
<h1 class="hero-title" data-aos="flip-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
Welcome to<br>the Four Wheel Drive Club<br>of Southern Africa
</h1>
<a href="membership" class="theme-btn style-two bgc-secondary" style="margin-top: 20px; background-color: #e90000; padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">
<span data-hover="Become a Member">Become a Member</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
</div>
</section>
<!-- Hero Area End -->
<!-- Destinations Area start -->
<?php
if (countUpcomingTrips() > 0) { ?>
<section class="destinations-area bgc-black pt-100 pb-70 rel z-1">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-title text-white text-center counter-text-wrap mb-70" data-aos="fade-up"
data-aos-duration="1500" data-aos-offset="50">
<h2>Discover Africa's Treasures with 4WDCSA</h2>
<p>Join us on the following trips:</p>
</div>
</div>
</div>
<div class="row justify-content-center">
<?php
// Query to retrieve data from the trips table
$sql = "SELECT trip_id, trip_name, location, short_description, start_date, end_date, vehicle_capacity, cost_members, places_booked FROM trips ORDER BY trip_id DESC LIMIT 4";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// Loop through each row
while ($row = $result->fetch_assoc()) {
$trip_id = $row['trip_id'];
$trip_name = $row['trip_name'];
$location = $row['location'];
$short_description = $row['short_description'];
$start_date = $row['start_date'];
$end_date = $row['end_date'];
$capacity = $row['vehicle_capacity'];
$cost_members = $row['cost_members'];
$places_booked = $row['places_booked'];
$remaining_places = $capacity - $places_booked;
// Determine the badge text based on the status
$badge_text = ($remaining_places > 0) ? $remaining_places . ' PLACES LEFT!!' : 'FULLY BOOKED';
echo '
<div class="col-xxl-3 col-xl-4 col-md-6">
<div class="destination-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<img src="assets/images/trips/' . $trip_id . '_01.jpg" alt="' . $trip_name . '">
</div>
<div class="content">
<span class="location"><i class="fal fa-map-marker-alt"></i> ' . $location . '</span>
<h5><a href="trip-details.php?trip_id=' . $trip_id . '">' . $trip_name . '</a></h5>
<span class="time">' . convertDate($start_date) . ' - ' . convertDate($end_date) . '</span><br>
<span class="time">' . calculateDaysAndNights($start_date, $end_date) . '</span>
</div>
<div class="destination-footer">
<span class="price"><span>R ' . $cost_members . '</span>/per member</span>
<a href="trip-details.php?trip_id=' . $trip_id . '" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
</div>
</div>
</div>';
}
} else {
echo "No trips available.";
}
?>
</div>
</div>
</section>
<!-- Destinations Area end -->
<?php
}
?>
<!-- About Us Area start -->
<section class="about-us-area py-100 rpb-90 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-xl-5 col-lg-6">
<div class="about-us-content rmb-55" data-aos="fade-left" data-aos-duration="1500"
data-aos-offset="50">
<div class="section-title mb-25">
<h2>Become a member of 4WDCSA</h2>
<p>Sign up for an annual membership and receive:</p>
<ul class="list-style-two mt-35 mb-30">
<li>Year round access to BASE4</li>
<li>FREE Camping at BASE4</li>
<li>Up to 95% Discount on Training Courses</li>
<li>Exclusive Member discounts for all trips and events</li>
<li>... and many more!</li>
</ul>
</div>
<p>We go above and beyond to make your travel dreams reality hidden gems and must-see
attractions</p>
<a href="membership" class="theme-btn mt-10 style-two">
<span data-hover="Become A Member">Become A Member</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
<div class="col-xl-7 col-lg-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="about-us-image">
<!-- <div class="shape"><img src="assets/images/about/shape1.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape2.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape3.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape4.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape5.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape6.png" alt="Shape"></div>
<div class="shape"><img src="assets/images/about/shape7.png" alt="Shape"></div> -->
<img src="assets/images/logos/weblogo.png" alt="About">
</div>
</div>
</div>
</div>
</section>
<!-- About Us Area end -->
<section class="hotel-area bgc-black py-100 rel z-1">
<div class="countdown-container">
<h1 style="color: #e5f5e0;" id="countdown">Loading countdown...</h1>
<a href="events" class="theme-btn style-two bgc-secondary" style="margin-top: 20px; background-color: #e90000; padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">
<span data-hover="Events">Find out more!</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</section>
<!-- Popular Destinations Area start -->
<!-- <section class="popular-destinations-area rel z-1">
<div class="container-fluid">
<div class="popular-destinations-wrap br-20 bgc-lighter pt-100 pb-70">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-title text-center counter-text-wrap mb-70" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<h2>Explore Popular Destinations</h2>
<p>One site <span class="count-text plus" data-speed="3000" data-stop="34500">0</span> most popular experience</p>
</div>
</div>
</div>
<div class="container">
<div class="row justify-content-center">
<div class="col-xl-3 col-md-6">
<div class="destination-item style-two" data-aos="flip-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
<img src="assets/images/destinations/destination1.jpg" alt="Destination">
</div>
<div class="content">
<h6><a href="destination-details.html">Thailand beach</a></h6>
<span class="time">5352+ tours & 856+ Activity</span>
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
<img src="assets/images/destinations/destination2.jpg" alt="Destination">
</div>
<div class="content">
<h6><a href="destination-details.html">Parga, Greece</a></h6>
<span class="time">5352+ tours & 856+ Activity</span>
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
<img src="assets/images/destinations/destination3.jpg" alt="Destination">
</div>
<div class="content">
<h6><a href="destination-details.html">Castellammare del Golfo, Italy</a></h6>
<span class="time">5352+ tours & 856+ Activity</span>
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="destination-item style-two" data-aos="flip-up" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
<img src="assets/images/destinations/destination4.jpg" alt="Destination">
</div>
<div class="content">
<h6><a href="destination-details.html">Reserve of Canada, Canada</a></h6>
<span class="time">5352+ tours & 856+ Activity</span>
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
<img src="assets/images/destinations/destination5.jpg" alt="Destination">
</div>
<div class="content">
<h6><a href="destination-details.html">Dubai united states</a></h6>
<span class="time">5352+ tours & 856+ Activity</span>
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="destination-item style-two" data-aos="flip-up" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<a href="#" class="heart"><i class="fas fa-heart"></i></a>
<img src="assets/images/destinations/destination6.jpg" alt="Destination">
</div>
<div class="content">
<h6><a href="destination-details.html">Milos, Greece</a></h6>
<span class="time">5352+ tours & 856+ Activity</span>
<a href="#" class="more"><i class="fas fa-chevron-right"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section> -->
<!-- Popular Destinations Area end -->
<!-- Features Area start -->
<section class="features-area pt-100 pb-45 rel z-1">
<div class="container">
<div class="row align-items-center">
<div class="col-xl-6">
<div class="features-content-part mb-55" data-aos="fade-left" data-aos-duration="1500"
data-aos-offset="50">
<div class="section-title mb-20">
<h2><b>BASE 4:</b> The home of 4WDCSA.</h2>
<p>Situated near the Hennops river, in Doornrandjie, Centurion.</p>
<div class="image">
<img style="border-radius:10px;" src="assets/images/base4/base4.jpg" alt="Hotel">
</div>
</div>
<div class="menu-btns py-10">
<a href="membership.php" class="theme-btn style-two bgc-secondary">
<span data-hover="Become a Member">Become a Member</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
<!-- <div class="menu-btns py-10">
<a href="campsite_booking" class="theme-btn style-two bgc-secondary">
<span data-hover="Book a Campsite">Book a Campsite</span>
<i class="fal fa-arrow-right"></i>
</a>
</div> -->
<!-- <div class="features-customer-box">
<div class="image">
<img src="assets/images/features/features-box.jpg" alt="Features">
</div>
<div class="content">
<div class="feature-authors mb-15">
<img src="assets/images/features/feature-author1.jpg" alt="Author">
<img src="assets/images/features/feature-author2.jpg" alt="Author">
<img src="assets/images/features/feature-author3.jpg" alt="Author">
<span>4k+</span>
</div>
<h6>850K+ Happy Customer</h6>
<div class="divider style-two counter-text-wrap my-25"><span><span class="count-text plus" data-speed="3000" data-stop="25">0</span> Years</span></div>
<p>We pride ourselves offering personalized itineraries</p>
</div>
</div> -->
</div>
</div>
<div class="col-xl-6" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="row pb-25">
<div class="col-md-6">
<div class="feature-item">
<div class="icon"><i class="flaticon-tent"></i></div>
<div class="content">
<h5><a href="trip-details.php">Club House</a></h5>
<p>We are currently in the process of building a new club house since the previous club house tragically burnt down in November of 2024.
</p>
</div>
</div>
<div class="feature-item">
<div class="icon"><i class="flaticon-tent"></i></div>
<div class="content">
<h5><a href="trip-details.php">4x4 Training Track</a></h5>
<p>Test your offroad driving skills on our training track with many obstacles
from rocky climbs, daring axle twisters, log bridge, side slopes and more!
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="feature-item mt-20">
<div class="icon"><i class="flaticon-tent"></i></div>
<div class="content">
<h5><a href="trip-details.php">24/7 Camping</a></h5>
<p>Pristene Camping grounds situated next to a stream, with ablutions, lapa and
communal fire pits.</p>
</div>
</div>
<div class="feature-item">
<div class="icon"><i class="flaticon-tent"></i></div>
<div class="content">
<h5><a href="trip-details.php">Swimming pool & Braai areas</a></h5>
<p>Unwind with a refreshing dip in our crystal-clear swimming pool or gather around the braai area for good food and great company</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Area end -->
<!-- Hotel Area start -->
<section class="hotel-area bgc-black py-100 rel z-1">
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-title text-white text-center counter-text-wrap mb-70" data-aos="fade-up"
data-aos-duration="1500" data-aos-offset="50">
<h2>Driver Training Courses</h2>
<p>Discover the in's and out's of your Four Wheel Drive with one of our dedicated training
courses:</p>
</div>
</div>
</div>
<div class="row justify-content-center">
<div class="col-xxl-6 col-xl-8 col-lg-10">
<div class="destination-item style-three" data-aos="fade-up" data-aos-duration="1500"
data-aos-offset="50">
<div class="image">
<!-- <div class="ratting"><i class="fas fa-star"></i> 4.8</div> -->
<!-- <a href="#" class="heart"><i class="fas fa-heart"></i></a> -->
<img src="assets/images/courses/driver_training.png" alt="Hotel">
</div>
<div class="content">
<span class="location"><i class="fal fa-map-marker-alt"></i> BASE4, Hennops</span>
<h5><a href="driver_training">Basic 4X4 Driver Training</a></h5>
<ul class="list-style-three">
<li>Master Off-Road Confidence</li>
<li>Hands-On Training</li>
<li>Safety First</li>
<!-- <li><i class="fal fa-router"></i> Internet</li> -->
</ul>
<div class="destination-footer">
<span class="price"><span>R <?= getPrice('driver_training', 'member'); ?></span>/for members</span>
<span class="price"><span>R <?= getPrice('driver_training', 'nonmember'); ?></span>/for non-members</span>
<a href="driver_training" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
</div>
</div>
</div>
</div>
<div class="col-xxl-6 col-xl-8 col-lg-10">
<div class="destination-item style-three" data-aos="fade-up" data-aos-delay="50"
data-aos-duration="1500" data-aos-offset="50">
<div class="image">
<!-- <div class="ratting"><i class="fas fa-star"></i> 4.8</div> -->
<!-- <a href="#" class="heart"><i class="fas fa-heart"></i></a> -->
<img src="assets/images/courses/bush_mechanics.png" alt="Hotel">
</div>
<div class="content">
<span class="location"><i class="fal fa-map-marker-alt"></i> BASE4, Hennops</span>
<h5><a href="bush_mechanics">Bush Mechanics Course</a></h5>
<ul class="list-style-three">
<li>Fix Your Vehicle in the Wild</li>
<li>Survival Skills for Off-Roaders</li>
<li>Hands-On Experience</li>
<!-- <li><i class="fal fa-router"></i> Internet</li> -->
</ul>
<div class="destination-footer">
<span class="price"><span>R <?= getPrice('bush_mechanics', 'member'); ?></span>/for members</span>
<span class="price"><span>R <?= getPrice('bush_mechanics', 'nonmember'); ?></span>/for non-members</span>
<a href="bush_mechanics" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
</div>
</div>
</div>
</div>
<div class="col-xxl-6 col-xl-8 col-lg-10">
<div class="destination-item style-three" data-aos="fade-up" data-aos-duration="1500"
data-aos-offset="50">
<div class="content">
<span class="location"><i class="fal fa-map-marker-alt"></i> BASE4, Hennops</span>
<h5><a href="rescue_recovery">Rescue & Recovery Course</a></h5>
<ul class="list-style-three">
<li>Master Advanced Recovery Techniques</li>
<li>Gain Confidence in High-Stress Situations</li>
<li>Teamwork and Communication</li>
<!-- <li><i class="fal fa-router"></i> Internet</li> -->
</ul>
<div class="destination-footer">
<span class="price"><span>R <?= getPrice('rescue_recovery', 'member'); ?></span>/for members</span>
<span class="price"><span>R <?= getPrice('rescue_recovery', 'nonmember'); ?></span>/for non-members</span>
<a href="rescue_recovery" class="read-more">Book Now <i class="fal fa-angle-right"></i></a>
</div>
</div>
<div class="image">
<!-- <div class="ratting"><i class="fas fa-star"></i> 4.8</div> -->
<!-- <a href="#" class="heart"><i class="fas fa-heart"></i></a> -->
<img src="assets/images/courses/rescue_recovery.png" alt="Hotel">
</div>
</div>
</div>
</div>
</div>
<!-- <div class="hotel-more-btn text-center mt-40">
<a href="destination2.html" class="theme-btn style-four">
<span data-hover="Explore More Hotel">Explore More Hotel</span>
<i class="fal fa-arrow-right"></i>
</a>
</div> -->
</div>
</section>
<!-- Hotel Area end -->
<!-- CTA Area start -->
<!-- <section class="cta-area pt-100 rel z-1">
<div class="container-fluid">
<div class="row">
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-duration="1500" data-aos-offset="50">
<div class="cta-item" style="background-image: url(assets/images/cta/cta1.jpg);">
<span class="category">Tent Camping</span>
<h2>Explore the world best tourism</h2>
<a href="trip-details.php" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Tours">Explore Tours</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="cta-item" style="background-image: url(assets/images/cta/cta2.jpg);">
<span class="category">Sea Beach</span>
<h2>World largest Sea Beach in Thailand</h2>
<a href="trip-details.php" class="theme-btn style-two">
<span data-hover="Explore Tours">Explore Tours</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
<div class="col-xl-4 col-md-6" data-aos="zoom-in-down" data-aos-delay="100" data-aos-duration="1500" data-aos-offset="50">
<div class="cta-item" style="background-image: url(assets/images/cta/cta3.jpg);">
<span class="category">Water Falls</span>
<h2>Largest Water falls Bali, Indonesia</h2>
<a href="trip-details.php" class="theme-btn style-two bgc-secondary">
<span data-hover="Explore Tours">Explore Tours</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
</div>
</div>
</section> -->
<!-- CTA Area end -->
<!-- Blog Area start -->
<section class="blog-area py-70 rel z-1">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-12">
<div class="section-title text-center counter-text-wrap mb-70" data-aos="fade-up"
data-aos-duration="1500" data-aos-offset="50">
<h2>Read about our past trips and events</h2>
</div>
</div>
</div>
<div class="row justify-content-center">
<?php
$sql = "SELECT blog_id, title, date, category, image, description, author, link, members_only FROM blogs ORDER BY date DESC LIMIT 3";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// Loop through each row
while ($row = $result->fetch_assoc()) {
$blog_id = $row['blog_id'];
$blog_title = $row['title'];
$blog_date = $row['date'];
$blog_category = $row['category'];
$blog_image = $row['image'];
$blog_description = $row['description'];
$blog_author = $row['author'];
$members_only = $row['members_only'];
if ($members_only) {
if (!isset($_SESSION['user_id'])) {
$blog_link = "login.php";
$button_hover = "Members Only";
$icon = "fa-lock";
} else {
if (getUserMemberStatus($_SESSION['user_id'])) {
$blog_link = $row['link'];
$button_hover = "Read More";
$icon = "fa-arrow-right";
} else {
$blog_link = "membership.php";
$button_hover = "Members Only";
$icon = "fa-lock";
}
}
} else {
$blog_link = $row['link'];
$button_hover = "Read More";
$icon = "fa-arrow-right";
}
echo '
<div class="col-xl-4 col-md-6">
<div class="blog-item" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="content">
<a href="#" class="category">' . $blog_category . '</a>
<h5><a href="' . $blog_link . '">' . $blog_title . '</a></h5>
<ul class="blog-meta">
<li><i class="far fa-calendar-alt"></i> <a href="#">' . $blog_date . '</a></li>
<li><i class="far fa-user"></i>' . getFullName($blog_author) . '</li>
</ul>
</div>
<div class="image">
<img style="border-radius:20px;" src="assets/images/blog/' . $blog_id . '/' . $blog_image . '" alt="Blog List">
</div>
<a style="width:100%;" href="' . $blog_link . '" class="theme-btn">
<span style="width:100%;" data-hover="' . $button_hover . '">Read More</span>
<i class="fal ' . $icon . '"></i>
</a>
</div>
</div>';
}
// Close connection
$conn->close();
} ?>
</div>
</div>
</section>
<!-- Blog Area end -->
<section class="bgc-black py-20 rel z-1">
<?php include_once('ad_banner.php'); ?>
</section>
<section class="py-20 rel z-1">
<?php include_once('logos.php'); ?>
</section>
<!-- footer area start -->
<footer class="main-footer bgs-cover overlay rel z-1 pb-25"
style="background-image: url(assets/images/backgrounds/footer.jpg);">
<div class="container">
<div class="footer-top pt-100 pb-30">
<div class="row justify-content-between">
<div class="col-xl-5 col-lg-6" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="footer-widget footer-contact">
<a href="https://chat.whatsapp.com/JD9xQuJlVX5AAJwcLrpl2B" target="_blank" style="text-decoration: none; color: inherit;">
<div style="display: flex; align-items: center; background-color: #e5f5e0; border-radius: 10px; padding: 10px; max-width: 100%; box-shadow: 0 2px 6px rgba(0,0,0,0.1);">
<img src="assets/images/icons/whatsapp.png" alt="WhatsApp" style="width: 64px; height: 64px; margin-right: 15px;">
<h1 style="margin: 0; font-size: 24px;">Join our WhatsApp Group</h1>
</div>
</a>
</div>
<div class="footer-widget footer-contact">
<div class="footer-title">
<h5>Get In Touch</h5>
</div>
<ul class="list-style-one">
<li><i class="fal fa-map-marked-alt"></i> Plot 50 Gemstone Rd, Doornrandje, Centurion, 0157</li>
<li><i class="fal fa-envelope"></i> <a
href="mailto:info@4wdcsa.co.za">info@4wdcsa.co.za</a></li>
<li><i class="fal fa-clock"></i> Mon - Fri, 09:00 - 17:00</li>
<li><i class="fal fa-phone-volume"></i> <a href="callto:+2779 065 2795">079 065 2795</a></li>
</ul>
</div>
</div>
<div class="col-xl-5 col-lg-6" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500"
data-aos-offset="50">
<div class="section-title counter-text-wrap mb-35">
<h2>Subscribe to our Mailing List</h2>
<p>Receive news and updates about upcoming trips and events.</p>
</div>
<div id="mc_embed_shell">
<div id="mc_embed_signup">
<form class="newsletter-form mb-50" action="https://fwdcsa.us17.list-manage.com/subscribe/post?u=3c26590bcc200ef52edc0bec2&amp;id=3c370893eb&amp;f_id=0099ebe3f0" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_self" novalidate="">
<div id="mc_embed_signup_scroll" style="width:100%;">
<div class="mc-field-group"></label><input type="email" name="EMAIL" class="required email" id="mce-EMAIL" required="" value="" placeholder="Email"></div>
<div class="mc-field-group"><input type="text" name="FNAME" class=" text" id="mce-FNAME" value="" placeholder="First Name"></div>
<div class="mc-field-group"><input type="text" name="LNAME" class=" text" id="mce-LNAME" value="" placeholder="Last Name"></div>
<div class="mc-field-group"><input type="text" name="PHONE" class="REQ_CSS" id="mce-PHONE" value="" placeholder="Phone Number"></div>
<div hidden=""><input type="hidden" name="tags" value="8324220"></div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display: none;"></div>
<div class="response" id="mce-success-response" style="display: none;"></div>
</div>
<div aria-hidden="true" style="position: absolute; left: -5000px;"><input type="text" name="b_3c26590bcc200ef52edc0bec2_3c370893eb" tabindex="-1" value=""></div>
<div class="clear"><input style="width:100%;" type="submit" name="subscribe" id="mc-embedded-subscribe" class="theme-btn bgc-secondary style-two" value="Subscribe"></div>
</div>
</form>
</div>
</div>
<!--
<form class="newsletter-form mb-50" action="#">
<input id="news-email" type="email" placeholder="Email Address" required>
<button type="submit" class="theme-btn bgc-secondary style-two">
<span data-hover="Subscribe">Subscribe</span>
<i class="fal fa-arrow-right"></i>
</button>
</form> -->
</div>
</div>
</div>
</div>
<div class="footer-bottom pt-20 pb-5">
<div class="container">
<div class="row">
<div class="col-lg-5">
<div class="copyright-text text-center text-lg-start">
<p>Copyright © <?php echo date("Y"); ?> <a href="index.html">4WDCSA</a> | All rights reserved.</p>
</div>
</div>
<div class="col-lg-7 text-center text-lg-end">
<ul class="footer-bottom-nav">
<li><a href="privacy_policy">Privacy Policy</a></li>
<!-- <li><a href="about.html">Terms</a></li> -->
<!-- <li><a href="about.html">Privacy Policy</a></li> -->
<!-- <li><a href="about.html">Legal notice</a></li> -->
<!-- <li><a href="about.html">Accessibility</a></li> -->
</ul>
</div>
</div>
<!-- Scroll Top Button -->
<button class="scroll-top scroll-to-target" data-target="html"><img
src="assets/images/icons/scroll-up.png" alt="Scroll Up"></button>
</div>
</div>
</footer>
<!-- footer area end -->
</div>
<!--End pagewrapper-->
<?php if ($indemnityPending): ?>
<!-- Bootstrap Modal -->
<div class="modal fade" id="indemnityModal" tabindex="-1" aria-labelledby="indemnityModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content border-secondary">
<div class="modal-header bg-secondary text-white">
<h5 class="modal-title" id="indemnityModalLabel">Membership Application Incomplete</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
To link your existing FWDCSA membership, you need to sign and accept the indemnity aggreement before proceeding.<br>
<a style="width:100%; border-radius:20px;" href="indemnity" class="btn btn-danger mt-3">Review and Accept</a>
</div>
</div>
</div>
</div>
<script>
// Show modal when page loads
document.addEventListener("DOMContentLoaded", function() {
var indemnityModal = new bootstrap.Modal(document.getElementById('indemnityModal'));
indemnityModal.show();
});
</script>
<?php endif; ?>
<!-- Jquery -->
<script src="assets/js/jquery-3.6.0.min.js"></script>
<!-- Bootstrap -->
<script src="assets/js/bootstrap.min.js"></script>
<!-- Appear Js -->
<script src="assets/js/appear.min.js"></script>
<!-- Slick -->
<script src="assets/js/slick.min.js"></script>
<!-- Magnific Popup -->
<script src="assets/js/jquery.magnific-popup.min.js"></script>
<!-- Nice Select -->
<script src="assets/js/jquery.nice-select.min.js"></script>
<!-- Image Loader -->
<script src="assets/js/imagesloaded.pkgd.min.js"></script>
<!-- Skillbar -->
<script src="assets/js/skill.bars.jquery.min.js"></script>
<!-- Isotope -->
<script src="assets/js/isotope.pkgd.min.js"></script>
<!-- AOS Animation -->
<script src="assets/js/aos.js"></script>
<!-- Custom script -->
<script src="assets/js/script.js"></script>
<script>
// Set your target date and time
const targetDate = new Date("<?php echo getNextOpenDayDate(); ?>T08:00:00"); // yyyy-mm-ddThh:mm:ss
function updateCountdown() {
const now = new Date();
const diff = targetDate - now;
if (diff <= 0) {
document.getElementById("countdown").innerHTML = "We're open now!";
return;
}
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff / (1000 * 60 * 60)) % 24);
const minutes = Math.floor((diff / (1000 * 60)) % 60);
const seconds = Math.floor((diff / 1000) % 60);
document.getElementById("countdown").innerHTML =
`${String(days).padStart(2, '0')} days ` +
`${String(hours).padStart(2, '0')} hours ` +
`${String(minutes).padStart(2, '0')} minutes ` +
`${String(seconds).padStart(2, '0')} seconds<br>` +
`till our next BASE4 Open Day!`;
}
updateCountdown(); // initial call
setInterval(updateCountdown, 1000);
</script>
</body>
</html>

14
src/pages/other/logos.php Normal file
View File

@@ -0,0 +1,14 @@
<div class="container my-4">
<div class="row justify-content-center align-items-center g-3">
<?php
$sponsorDir = 'assets/images/sponsors/';
$sponsorImages = glob($sponsorDir . '*.{jpg,jpeg,png,gif,svg,webp}', GLOB_BRACE);
foreach ($sponsorImages as $img) {
echo "
<div class='col-auto'>
<img src='$img' alt='Sponsor logo' style='max-height:120px; max-width:240px; object-fit:contain;'>
</div>";
}
?>
</div>
</div>

91
src/pages/other/modal.php Normal file
View File

@@ -0,0 +1,91 @@
<?php
require_once("env.php");
require_once("session.php");
require_once("connection.php");
require_once("functions.php");?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modal with AJAX Dropdown</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<div class="container mt-5">
<!-- Button to trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#userModal">
Open Modal
</button>
</div>
<!-- Modal -->
<div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="userModalLabel">Select a User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="barTabForm">
<div class="mb-3">
<label for="userSelect" class="form-label">Choose a User</label>
<select class="form-select" id="userSelect" name="user_id" required>
<option value="">Loading...</option>
</select>
</div>
<button type="submit" class="btn btn-success">Create Bar Tab</button>
</form>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
// Load users into dropdown when modal opens
$('#userModal').on('shown.bs.modal', function () {
$.ajax({
url: 'fetch_users',
method: 'GET',
dataType: 'json',
success: function (data) {
let dropdown = $('#userSelect');
dropdown.empty();
dropdown.append('<option value="">Select a user</option>');
data.forEach(user => {
dropdown.append(`<option value="${user.id}">${user.first_name} ${user.last_name}</option>`);
});
},
error: function () {
alert('Error fetching users.');
}
});
});
// Handle form submission
$('#barTabForm').submit(function (e) {
e.preventDefault(); // Prevent default form submission
$.ajax({
url: 'create_bar_tab',
method: 'POST',
data: $(this).serialize(),
success: function (response) {
alert('Bar tab created successfully!');
$('#userModal').modal('hide'); // Close modal
},
error: function () {
alert('Error creating bar tab.');
}
});
});
});
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,317 @@
<?php
$headerStyle = 'light';
$rootPath = dirname(dirname(dirname(__DIR__)));
include_once($rootPath . '/header.php');
checkUserSession();
// SQL query to fetch dates for rescue & recovery
$stmt = $conn->prepare("SELECT course_id, date FROM courses WHERE course_type = ? AND date >= CURDATE()");
$course_type = 'rescue_recovery';
$stmt->bind_param("s", $course_type);
$stmt->execute();
$result = $stmt->get_result();
$page_id = 'rescue_recovery';
?>
<style>
.form-group {
margin-bottom: 15px;
}
select {
width: 100%;
padding: 8px;
font-size: 16px;
}
</style><?php
$pageTitle = 'Rescue & Recovery';
$breadcrumbs = [['Home' => 'index.php']];
require_once($rootPath . '/components/banner.php');
?>
<!-- Product Details Start -->
<section class="product-details pt-100">
<div class="container">
<div class="row">
<div class="col-lg-6">
<div class="product-details-images rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-content preview-images">
<div class="tab-pane fade preview-item active show" id="preview1">
<img src="assets/images/drivertraining/01.jpg" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview2">
<img src="assets/images/drivertraining/02.jpg" alt="Perview">
</div>
<div class="tab-pane fade preview-item" id="preview3">
<img src="assets/images/drivertraining/03.jpg" alt="Perview">
</div>
</div>
<div class="nav thumb-images rmb-20">
<a href="#preview1" data-bs-toggle="tab" class="thumb-item active show">
<img src="assets/images/drivertraining/01.jpg" alt="Thumb">
</a>
<a href="#preview2" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/drivertraining/02.jpg" alt="Thumb">
</a>
<a href="#preview3" data-bs-toggle="tab" class="thumb-item">
<img src="assets/images/drivertraining/03.jpg" alt="Thumb">
</a>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="product-details-content" data-aos="fade-right" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title">
<h2>Rescue & Recovery</h2>
</div>
<!-- <div class="ratting mb-15">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
<span>(5.8k+ reviews)</span>
</div> -->
<span class="price mb-5">R <?= getPrice('rescue_recovery', 'member'); ?>/member</span>
<span class="price mb-25">R <?= getPrice('rescue_recovery', 'nonmember'); ?>/non-members</span>
<p> This is an enjoyable, educational, and informative day. This one-day course is a natural follow-on from our 4x4 driving course and introduces drivers to safe off-road recovery procedures, equipment and also their responsibilities in regards to caring for the environment.</p>
<hr class="mt-40">
<div class="blog-sidebar tour-sidebar">
<div class="widget widget-booking" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<form action="process_course_booking" method="POST">
<ul class="tickets clearfix">
<li>
Select Date
<select name="course_id" id="course_id" required>
<?php
if ($result->num_rows > 0) {
// Output each course as an option
while ($row = $result->fetch_assoc()) {
$course_id = htmlspecialchars($row['course_id']); // Escape output for security
$date = htmlspecialchars($row['date']); // Escape output for security
echo "<option value='$course_id'>$date</option>";
}
} else {
echo "<option value='' disabled>No dates available</option>";
}
?>
</select>
</li>
<?php
if ($is_member || $pending_member) {
echo '
<li>
Additional Members <span class="price"></span>
<select name="members" id="members">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
';
} ?>
<li>
Additional Non-Members <span class="price"></span>
<select name="non-members" id="non-members">
<option value="0" selected>00</option>
<option value="1">01</option>
<option value="2">02</option>
<option value="3">03</option>
</select>
</li>
</ul>
<hr class="mb-25">
<h6>Total: <span id="booking_total" class="price">-</span></h6>
<div style="margin: 20px 0;">
<div id="indemnityBox" style="border: 1px solid #ccc; padding: 10px; height: 150px; overflow-y: scroll; background: #f9f9f9; font-size: 12px;">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div style="margin-top: 10px;">
<input type="checkbox" id="agreeCheckbox" name="agree" disabled required>
<label for="agreeCheckbox" id="agreeLabel" style="color: #888;">I have read and agree to the indemnity terms</label>
</div>
</div>
<?php
$button_text = "Book Now";
$button_disabled = "";
if (!$result || $result->num_rows == 0) {
$button_text = "No booking dates available";
$button_disabled = "disabled";
}
?>
<button type="submit" class="theme-btn style-two w-100 mt-15 mb-5" <?php echo $button_disabled; ?>>
<span data-hover="<?php echo $button_text; ?>"><?php echo $button_text; ?></span>
<i class="fal fa-arrow-right"></i>
</button>
<div class="text-center">
<a href="mailto:info@4wdcsa.co.za">Need some help?</a>
</div>
</form>
</div>
</div>
<!-- <hr class="mb-45"> -->
<!-- <a href="#" class="wishlist"><i class="far fa-heart"></i> Add to Wishlist</a> -->
</div>
</div>
</div>
<ul class="nav nav product-tab mt-70 mb-30" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<li><a href="#details" data-bs-toggle="tab" class="active show">Course Overview<i class="far fa-arrow-right"></i></a></li>
<li><a href="#information" data-bs-toggle="tab">What to Expect<i class="far fa-arrow-right"></i></a></li>
<li><a href="#reviews" data-bs-toggle="tab"> Reviews <i class="far fa-arrow-right"></i></a></li>
</ul>
<div class="tab-content" data-aos="fade-up" data-aos-delay="50" data-aos-duration="1500" data-aos-offset="50">
<div class="tab-pane fade active show" id="details">
<div class="container my-5">
<h5>The course introduces and guides participants in the following:</h5>
<ul >
<li>Correct selection, size and purchase of equipment such as Shackles, Slings, Chains and Snatch Blocks.</li>
<li>Correct selection of Pull Straps and Kinetic Straps.</li>
<li>Importance of Safety Slings, Safety Blankets and the correct use of Bridles.</li>
<li>
Importance of secondary equipment such as:</li>
<ul>
<li>Puncture Repair Kits</li>
<li>Tyre Pressure Gauges</li>
<li>Compressors</li>
<li>Fire Extinguishers</li>
</ul>
</ul>
<h5>The day also covers the practical instruction in the following:</h5>
<ul class="list-unstyled ms-4" style="list-style-type: disc;">
<li>Use of Pull straps and Kinetic Straps</li>
<li>Recovery Points and their importance.</li>
<li>Jacking of vehicles OEM, High Lift, Bottle Jacks and Scissor Jacks.</li>
<li>Correct use of Winches including the accessories.</li>
</ul>
<h5>The day culminates with an impressive demonstration of a kinetic strap failure and the repercussion.</h5>
<p><strong>PLEASE NOTE </strong>- <i>Recovery equipment is provided for use by participants during the course. If you already have your own equipment, you are welcome to make use of it during training.</i></p>
<p>Please bring your own lunch and refreshments, sun hats and sunblock.</p>
</div>
<div class="row gap-50 pt-25 pb-20 align-items-center">
<div class="col-lg-7 pt-15">
<h5>What this course includes</h5>
<ul class="list-style-two mt-25">
<li>Rescue & Recovery Manual.</li>
<li>Theory session and discussion.</li>
<li>Spend the afternoon practicing common techniques of advanced rescue and recovery.</li>
</ul>
</div>
<div class="col-lg-5">
<div class="image rmt-45">
<img src="assets/images/drivertraining/04.jpg" alt="Product Details">
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="information">
<!-- <p>Circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses</p> -->
<ul class="list-style-two my-35">
<li>Coffee and Welcome: Kick off the day with a warm coffee, meet your instructors, and receive an overview of the course schedule</li>
<li>Theory Session: Dive into the key principles of off-road driving, including vehicle mechanics, terrain navigation, recovery methods, and safety protocols.</li>
<li>Practical Demonstrations: Watch live demonstrations covering vital techniques like gear selection, adjusting tire pressure, and setting up recovery equipment.</li>
<li>Lunch Break: Enjoy a packed lunch or bring something to braai. Fires will be provided for an authentic outdoor experience.</li>
<li>Track Driving and Practical Training: Test your skills with hands-on driving exercises on a custom-built off-road track, learning how to tackle different obstacles and terrain challenges.</li>
<li>Debrief and Certificates: Conclude the day with a review of your progress, feedback from the instructors, and certificates of completion for your off-road training.</li>
</ul>
</div>
<div class="tab-pane fade mb-20" id="reviews">
<?php include_once('review_box.php'); ?>
</div>
</div>
</div>
</section>
<!-- Shop Details Area end -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
const indemnityBox = document.getElementById('indemnityBox');
const agreeCheckbox = document.getElementById('agreeCheckbox');
const bookingForm = document.querySelector('form');
indemnityBox.addEventListener('scroll', function() {
const scrollTop = indemnityBox.scrollTop;
const scrollHeight = indemnityBox.scrollHeight;
const offsetHeight = indemnityBox.offsetHeight;
// Enable checkbox when scrolled to bottom
if (scrollTop + offsetHeight >= scrollHeight - 1) {
agreeCheckbox.disabled = false;
document.getElementById('agreeLabel').style.color = "#000"; // optional: make label active
}
});
bookingForm.addEventListener('submit', function(e) {
if (agreeCheckbox.disabled || !agreeCheckbox.checked) {
alert('Please read and agree to the indemnity terms before booking.');
e.preventDefault(); // stop form submission
}
});
</script>
<script>
$(document).ready(function() {
// Function to calculate booking total
function calculateTotal() {
// Get selected values from the form
var members = parseInt($('#members').val()) || 0; // Default to 1 vehicle if not selected
var nonmembers = parseInt($('#non-members').val()) || 0; // Default to 1 adult if not selected
// Fetch PHP variables
var isMember = <?php echo $is_member ? 'true' : 'false'; ?>;
var pendingMember = <?php echo $pending_member ? 'true' : 'false'; ?>;
var cost_members = <?= getPrice('rescue_recovery', 'member'); ?>;
var cost_nonmembers = <?= getPrice('rescue_recovery', 'nonmember'); ?>;
// Calculate the total cost based on membership
var total = 0;
// Calculate cost for members
if (isMember || pendingMember) {
total = (cost_members) + (members * cost_members) + (nonmembers * cost_nonmembers);
} else {
// Calculate cost for non-members
total = (cost_nonmembers) + (members * cost_members) + (nonmembers * cost_nonmembers);
}
// Update total price in the DOM
$('#booking_total').text('R ' + total.toFixed(2));
}
// Event listeners to trigger recalculation when any form field changes
$('#members, #non-members').on('change', function() {
calculateTotal();
});
// Initial calculation on page load
calculateTotal();
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,152 @@
<?php
if (!isset($page_id)) {
die("Page ID not set for comment system.");
}
$conn = openDatabaseConnection();
// Handle comment post
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_comment'])) {
$comment = $conn->real_escape_string(trim($_POST['comment']));
if (!empty($comment)) {
$stmt = $conn->prepare("INSERT INTO comments (page_id, user_id, comment) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $page_id, $user_id, $comment);
if ($stmt->execute()) {
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
}
}
// Fetch comments
$stmt = $conn->prepare("SELECT user_id, comment, created_at FROM comments WHERE page_id = ? ORDER BY created_at DESC");
$stmt->bind_param("s", $page_id);
$stmt->execute();
$result = $stmt->get_result();
?>
<div>
<h5>Reviews</h5>
<div class="comments my-30">
<?php while ($row = $result->fetch_assoc()): ?>
<div class="comment-body" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div>
<img class="profile-pic" src="<?= getProfilePic($user_id); ?>" alt="Author">
</div>
<div class="content">
<h6><?= getFullName($row['user_id']); ?></h6>
<?php
if (getUserMemberStatus($row['user_id'])){
echo '<div class="badge badge-primary badge-pill">MEMBER</div>';
}
?>
<em><?= $row['created_at'] ?></em>
<!-- <div class="ratting">
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star"></i>
<i class="fas fa-star-half-alt"></i>
</div> -->
<p><?= nl2br(htmlspecialchars($row['comment'])) ?></p>
<!-- <a class="read-more" href="#">Reply <i class="far fa-angle-right"></i></a> -->
</div>
</div>
<?php endwhile; ?>
</div>
<h5>Add A Review</h5>
<form method="POST" id="comment-form" class="comment-form bgc-lighter z-1 rel mt-30" name="review-form" action="" data-aos="fade-up" data-aos-duration="1500" data-aos-offset="50">
<div class="row gap-20">
<div class="col-md-12">
<div class="form-group">
<textarea name="comment" id="comment" class="form-control" rows="5" placeholder="Add review..." required></textarea>
</div>
</div>
<div class="col-md-12">
<div class="form-group mb-0">
<button type="submit" name="submit_comment" class="theme-btn bgc-secondary style-two">
<span data-hover="Submit reviews">Submit review</span>
<i class="fal fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</form>
</div>
<style>
.comment-box {
border: 1px solid #ccc;
padding: 10px;
max-width: 600px;
}
.comment-box form input,
.comment-box form textarea {
width: 100%;
margin-bottom: 8px;
}
.comments-list {
margin-top: 20px;
}
.comment {
border-top: 1px solid #eee;
padding-top: 10px;
margin-top: 10px;
}
.profile-pic {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
/* Ensures the image fits without distortion */
}
.badge {
display: inline-block;
padding: 0.4em 0.8em;
font-size: 0.875rem;
font-weight: 600;
color: white;
border-radius: 0.375em;
margin-right: 0.5em;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-primary {
background-color: #e90000;
}
.badge-success {
background-color: #28a745;
}
.badge-warning {
background-color: #ffc107;
color: #212529;
}
.badge-danger {
background-color: #dc3545;
}
.badge-info {
background-color: #17a2b8;
}
.badge-pill {
border-radius: 999px;
}
</style>

View File

@@ -0,0 +1,104 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
// Assuming you have the user ID stored in the session
if (isset($_SESSION['user_id'])) {
$user_id = $_SESSION['user_id'];
} else {
header('Location: login.php');
exit(); // Stop further script execution
}
?>
<style>
h2 {
text-align: center;
margin-bottom: 20px;
}
.indemnitycontainer {
max-width: 800px;
margin: 0 auto;
}
.indemnity-text {
text-align: justify;
margin-bottom: 20px;
}
.signature-container {
margin-top: 30px;
margin-bottom: 100px;
text-align: center;
}
#signature-pad {
border: 1px solid black;
}
</style>
<!-- Page Banner Start -->
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="container">
<div class="banner-inner text-white">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Indemnity</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index">Home</a></li>
<li class="breadcrumb-item ">Membership</li>
<li class="breadcrumb-item active">Indemnity</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- Page Banner End -->
<div class="indemnitycontainer pt-20">
<!-- <h2>Indemnity and Waiver</h2> -->
<div class="indemnity-text">
<p><strong>INDEMNITY AND WAIVER</strong></p>
<p>1. I agree to abide by the Code of Conduct as listed below, as well as any reasonable instructions given by any Member of the Committee of the Club, or any person appointed by the Club to organise or control any event (Club Officer).</p>
<p>2. I acknowledge that driving the off-road track is inherently dangerous, and that I am fully aware of the dangers thereof. I warrant that I will make all members of my party aware of such dangers prior to driving the track.</p>
<p>3. While I, or any member of my party, enjoy the facilities at Base 4 including overnight camping, picnicking, driving the track, using the swimming pool facility or activity or any other activity while at Base 4, I agree that under no circumstances shall the Club be liable for any loss or damage of any kind whatsoever (including consequential loss) which I or any of my party may suffer, regardless of how such loss or damage may have been caused or sustained, and whether or not as a result of the negligence or breach of contract (whether fundamental or otherwise) or other wrongful act of the Club, or any Club Officer, or any of the Clubs agents or contractors, and I hereby indemnify and hold harmless the Club and any Club Officer against all such loss or damage.</p>
<p>4. The expression, member of my party, means all persons who accompany me or attending any event at my specific invitation, request or suggestion, and includes without limitation, members of family, guests and invitees.</p>
<p>5. I understand that I am responsible for ensuring my vehicle and equipment and that all members of my party have adequate health and medical insurance to cover any and all likely occurrences.</p>
<p>6. This indemnity is irrevocable and shall apply to me and the members of my party for any Club events in which I may participate or attend.</p>
<p><strong>BASE 4 CODE OF CONDUCT</strong></p>
<p>1. No motorbikes or quadbikes.</p>
<p>2. No loud music (unless authorised by the Committee or its representatives).</p>
<p>3. Dogs to be controlled by their owners who take full responsibility for the animals behaviour.</p>
<p>4. No dogs belonging to non-members are allowed at Base 4 unless with the express permission of the Committee.</p>
<p>5. No person in the rear of open vehicles when driving on obstacles.</p>
<p>6. When driving the obstacles stay on the tracks.</p>
<p>7. Engage 4WD when driving the obstacles to minimise wear and damage to the track.</p>
<p>8. No alcohol to be consumed while driving the track.</p>
<p>9. No littering (please pick up cigarette butts etc.)</p>
<p>10. All rubbish is to be taken away with you when leaving. Dustbins and refuse collection is not provided.</p>
<p>11. Use water sparingly. Please bring your own water and a little extra for the Club.</p>
<p>I am a member of the Four Wheel Drive Club of Southern Africa and I will strive to uphold these Codes.</p>
</div>
<div class="signature-container">
<div style="text-align: center;" id="responseMessage"></div> <!-- Message display area -->
<p><strong>Signature</strong></p>
<img src="uploads/signatures/signature_<?php echo $user_id;?>.png" alt="Signature" width="200">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.0.0/dist/signature_pad.umd.min.js"></script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php') ?>

View File

@@ -0,0 +1,36 @@
<?php
session_start();
require_once("env.php");
require_once("connection.php");
if (isset($_POST['tab_id']) && isset($_POST['item_id']) && isset($_POST['item_name']) && isset($_POST['item_price'])) {
$tab_id = mysqli_real_escape_string($conn, $_POST['tab_id']);
$item_id = mysqli_real_escape_string($conn, $_POST['item_id']);
$item_name = mysqli_real_escape_string($conn, $_POST['item_name']);
$item_price = mysqli_real_escape_string($conn, $_POST['item_price']);
$user_id = mysqli_real_escape_string($conn, $_POST['user_id']);
// Initialize cart session if not set
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
}
// Add the drink to the cart for the given tab
if (!isset($_SESSION['cart'][$tab_id])) {
$_SESSION['cart'][$tab_id] = [];
}
// Add the drink as an associative array
$_SESSION['cart'][$tab_id][] = [
'item_id' => $item_id,
'item_name' => $item_name,
'item_price' => $item_price,
'user_id' => $user_id
];
echo json_encode(['status' => 'success', 'cart' => $_SESSION['cart']]);
} else {
echo json_encode(['status' => 'error', 'message' => 'Missing required parameters.']);
}
?>

485
src/pages/shop/bar_tabs.php Normal file
View File

@@ -0,0 +1,485 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkUserSession();
$user_id = $_SESSION['user_id'];
unset($_SESSION['cart']);
?>
<!-- Include jQuery UI CSS (required for autocomplete) -->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />
<!-- Include jQuery and jQuery UI -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<style>
.modal {
z-index: 1050 !important;
/* Ensures it's on top */
}
.modal-backdrop {
z-index: 0 !important;
/* Keeps the backdrop below */
opacity: 0 !important;
/* Adjust if necessary */
}
</style>
<style>
/* Style the autocomplete container */
.ui-autocomplete {
/* background-color: #fff; */
/* border: 1px solid #ccc; */
/* max-height: 200px; */
/* overflow-y: auto; */
/* width: 100%; */
/* position: absolute; */
/* z-index: 9999; */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
/* Style the autocomplete suggestion items */
.ui-menu .ui-menu-item {
font-family: var(--base-font);
background-color: #fff;
border: 1px solid #ccc;
/* padding: 8px; */
cursor: pointer;
}
/* Hover effect for suggestions */
.ui-menu .ui-menu-item:hover {
background-color: rgb(207, 81, 81);
}
/* Selected item in autocomplete */
.ui-state-focus {
background-color: #dcdcdc;
color: #000;
}
/* Style the input field for better user experience */
#userSelect {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
width: 100%;
}
.profile-pic {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
/* Ensures the image fits without distortion */
}
.drinks-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.drink-option {
width: 180px;
text-align: center;
}
</style>
<!-- About Us Area start -->
<section class="about-us-area pt-90 pb-100 rel z-1">
<div class="container">
<div class="row gap-100 align-items-center">
<div class="col-lg-12">
<div class="destination-details-content rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="row">
<div class="col-lg-6 section-title mb-25">
<span class="h2 mb-15">BAR TABS</span>
</div>
<div id="tabTotalContainer" class="col-lg-6 section-title mb-25 text-end" style="display: none;">
<span id="tabTotal" class="h2 mb-15">TAB TOTAL: R 0.00</span>
</div>
</div>
<!-- Button to trigger modal -->
<div id="newTabButton">
<button type="button" class="theme-btn style-two bgc-secondary" style="width:100%; margin-top: 20px; background-color:rgb(80, 155, 82); padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;" data-bs-toggle="modal" data-bs-target="#userModal" data-bs-backdrop="false">
NEW BAR TAB
</button>
</div>
<!-- Bar Tabs Container -->
<div id="barTabsContainer" class="mt-4">
<div id="barTabsList" class="d-flex flex-wrap gap-3">
<!-- Dynamic Bar Tabs will be loaded here -->
</div>
</div>
<div class="row">
<div class="col-lg-9">
<input type="hidden" id="selectedTabId">
<input type="hidden" id="selectedUserId">
<!-- Drinks Container for the Selected Tab -->
<div id="drinksContainer" class="drinks-container" style="display: none;">
<!-- Drinks will be dynamically inserted here -->
</div>
</div>
<div class="col-lg-3">
<!-- Cart Section (Optional) -->
<div id="cartContainer" class="cart-container p-3 bg-light border rounded" style="display: none; height:100%">
<h4 id="orderTotal">Order:</h4>
<ul id="cartList"></ul>
</div>
</div>
<div class="col-lg-12" id="submitButton" style="display: none;">
<button id="submitOrder" class="btn btn-success" style="width:100%; margin-top: 20px; background-color:rgb(80, 155, 82); padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">Submit Order</button>
</div>
</div>
<!-- Modal -->
</div>
</div>
</div>
</div>
</section>
<div class="modal fade" id="userModal" tabindex="-1" aria-labelledby="userModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="userModalLabel">Choose a Member</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="barTabForm">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="form-group">
<label for="userSelect">Select User</label>
<input type="text" id="userSelect" class="form-control" placeholder="Search User" required>
<!-- Hidden input for user_id -->
<input type="hidden" name="user_id" id="user_id" />
</div>
<button type="submit" class="theme-btn style-two bgc-secondary" style="width:100%; margin-top: 20px; background-color:rgb(80, 155, 82); padding: 10px 20px; color: white; text-decoration: none; border-radius: 25px;">Create Bar Tab</button>
</form>
</div>
</div>
</div>
</div>
<!-- About Us Area end -->
<script>
$(document).ready(function() {
$('#userSelect').autocomplete({
source: function(request, response) {
$.ajax({
url: 'fetch_users',
method: 'GET',
dataType: 'json',
success: function(data) {
// Filter the data based on the search query
var filteredUsers = data.filter(user => {
return user.first_name.toLowerCase().includes(request.term.toLowerCase()) ||
user.last_name.toLowerCase().includes(request.term.toLowerCase());
});
response(filteredUsers.map(user => ({
label: `${user.first_name} ${user.last_name}`, // Display name
name: `${user.first_name} ${user.last_name}`, // Display name
value: user.user_id // Use user_id for selection
})));
},
error: function() {
alert('Error fetching users.');
}
});
},
minLength: 1, // Start searching after typing 1 character
select: function(event, ui) {
// Set the selected user's name in the input field
$('#userSelect').val(ui.item.name); // Display name in the input field
// Set the user ID value in the hidden input field
$('#user_id').val(ui.item.value); // Store the user_id in the hidden input
console.log('User ID: ' + ui.item.value); // Log the selected user_id
console.log('User Name: ' + ui.item.name); // Log the selected user name
},
focus: function(event, ui) {
// Prevent the input field from showing the user_id when selecting an item
$('#userSelect').val(ui.item.name); // Always show the user's name in the input
}
});
// Handle form submission to create a new bar tab
$('#barTabForm').submit(function(e) {
e.preventDefault(); // Prevent default form submission
$.ajax({
url: 'create_bar_tab',
method: 'POST',
data: $(this).serialize(), // Send form data, including user_id
dataType: 'json',
success: function(response) {
if (response.status === 'success') {
// alert('Bar tab created successfully!');
$('#userModal').modal('hide'); // Close modal if applicable
// Reload the bar tabs after creation
loadBarTabs();
} else {
alert('Tab already exists for this member.');
}
},
error: function() {
alert('Error creating bar tab.');
}
});
});
// Fetch and render bar tabs
function loadBarTabs() {
$.ajax({
url: 'fetch_bar_tabs',
method: 'GET',
dataType: 'json',
success: function(data) {
if (data.length > 0) {
let tabsHtml = '';
data.forEach(function(barTab) {
tabsHtml += `
<div class="bar-tab-card p-3 bg-light border rounded" data-bar-tab-id="${barTab.tab_id}" data-user-id="${barTab.user_id}" style="cursor: pointer; width: 180px;">
<img src="assets/images/pp/${barTab.profile_pic}" alt="Profile Image" class="profile-pic" style="width: 150px; height: 150px;">
<h3 class="mb-0 font-weight-bold">${barTab.first_name} ${barTab.last_name}</h3>
</div>
`;
});
// Update the bar tabs list container
$('#barTabsList').html(tabsHtml);
} else {
$('#barTabsList').html('<p>No bar tabs available.</p>');
}
},
error: function() {
alert('Error fetching bar tabs.');
}
});
}
// Load the bar tabs on page load
loadBarTabs();
$(document).on('change', '#selectedTabId', function() {
var tabId = $(this).val();
if (tabId) {
fetchTabTotal(tabId);
}
});
// Handle bar tab clicks and display the drinks container
$(document).on('click', '.bar-tab-card', function() {
var tabId = $(this).data('bar-tab-id');
var userId = $(this).data('user-id');
console.log(tabId);
$('#selectedTabId').val(tabId);
$('#selectedUserId').val(userId);
fetchTabTotal(tabId);
// Fetch available drinks for the selected tab
$.ajax({
url: 'fetch_drinks',
method: 'GET',
data: {
tab_id: tabId
},
dataType: 'json',
success: function(drinks) {
displayDrinks(drinks);
$('#newTabButton').hide(); // Show the drinks container
$('#barTabsContainer').hide(); // Show the drinks container
$('#drinksContainer').show(); // Show the drinks container
$('#cartContainer').show(); // Show the cart container
$('#submitButton').show(); // Show the cart container
$('#tabTotalContainer').show(); // Show the cart container
}
});
});
// Display the drinks dynamically
function displayDrinks(drinks) {
var drinksHtml = '';
drinks.forEach(function(drink) {
drinksHtml += `
<div class="drink-option p-3 bg-light border rounded text-center"
data-item-id="${drink.item_id}"
data-item-price="${drink.price}"
data-item-name="${drink.description}"
style="width: 180px; flex: 0 0 auto; cursor: pointer;">
<img src="assets/images/bar/${drink.image}" alt="${drink.description}" class="drink-image"
style="width: 150px; height: 150px;">
<p>${drink.description}</p>
<h3>R ${drink.price}</h3>
</div>
`;
});
// Insert the drinks into the container and show it
$('#drinksContainer').html(drinksHtml).show();
// Add click event to each drink option
$('.drink-option').click(function() {
var drinkId = $(this).data('item-id');
var drinkPrice = $(this).data('item-price');
var drinkName = $(this).data('item-name');
var tabId = $('#selectedTabId').val();
var userId = $('#selectedUserId').val();
console.log('Clicked Drink ID:', drinkName);
console.log('Tab ID:', tabId);
if (!drinkId || !tabId) {
alert('Missing tab or drink ID. Cannot add to cart.');
return;
}
// Add the drink to the cart (session)
$.ajax({
url: 'add_to_cart',
method: 'POST',
data: {
tab_id: tabId,
user_id: userId,
item_id: drinkId,
item_price: drinkPrice,
item_name: drinkName
},
dataType: 'json',
success: function(response) {
if (response.status === 'success') {
updateCartUI(response.cart); // Update the cart UI with the added drink
} else {
console.error('Error response from server:', response);
alert('Error adding drink to cart.');
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.error('AJAX request failed. Status:', textStatus, 'Error:', errorThrown);
alert('There was an error with the request. Check console for details.');
}
});
});
}
// Update the cart UI with the selected drinks
function updateCartUI(cart) {
var cartListHtml = '';
var totalPrice = 0; // Initialize total price
console.log("Cart Data:", cart);
// Iterate over each tab in the cart
Object.keys(cart).forEach(function(tabId) {
cartListHtml += `<li><strong>Tab ID: ${tabId}</strong></li>`;
// Iterate over each drink in this tab
cart[tabId].forEach(function(drink) {
cartListHtml += `
<li class="d-flex justify-content-between">
<span>${drink.item_name}</span>
<span>R ${parseFloat(drink.item_price).toFixed(2)}</span>
</li>
`;
totalPrice += parseFloat(drink.item_price); // Add drink price to total
});
});
// Update the cart list and total price in the UI
$('#cartList').html(cartListHtml);
$('#orderTotal').html(`Order Total: <strong>R ${totalPrice.toFixed(2)}</strong>`);
// Show the cart container if there are items
if (totalPrice > 0) {
$('#cartContainer').show();
} else {
$('#cartContainer').hide();
}
}
// Submit the order
$('#submitOrder').click(function() {
var tabId = $('#selectedTabId').val();
// Submit the order
$.ajax({
url: 'submit_order',
method: 'POST',
data: {
tab_id: tabId
},
dataType: 'json',
success: function(response) {
if (response.status === 'success') {
// alert('Order submitted successfully!');
$('#cartList').html('');
$('#orderTotal').html('Order Total:');
loadBarTabs(); // Optionally reload the bar tabs
$('#barTabsContainer').show();
$('#newTabButton').show(); // Show the drinks container
$('#drinksContainer').hide();
$('#cartContainer').hide();
$('#submitButton').hide(); // Show the cart container
$('#tabTotalContainer').hide(); // Show the cart container
} else {
// Display error messages
var errorMessage = 'Error submitting order.';
if (response.errors && response.errors.length > 0) {
errorMessage += '\n' + response.errors.join('\n'); // Concatenate all errors
}
alert(errorMessage);
// Optionally display errors in a div (if you have an error container)
$('#orderErrorContainer').html('<div class="alert alert-danger">' + errorMessage.replace(/\n/g, '<br>') + '</div>');
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.error('AJAX request failed. Status:', textStatus, 'Error:', errorThrown);
console.error('Response:', jqXHR.responseText);
alert('There was an error with the request. Check console for details.');
}
});
});
function fetchTabTotal(tabId) {
console.log("fetching tab total...")
$.ajax({
url: 'get_tab_total',
method: 'POST',
data: {
tab_id: tabId
},
dataType: 'json',
success: function(response) {
if (response.status === 'success') {
$('#tabTotal').html(`<strong>Total: R ${response.total}</strong>`);
} else {
console.error(response.message);
$('#tabTotal').html('<strong>Error fetching total</strong>');
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.error('AJAX error:', textStatus, errorThrown);
}
});
}
});
</script>
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

326
src/pages/shop/confirm.php Normal file
View File

@@ -0,0 +1,326 @@
<?php
// $pfHost = 'www.payfast.co.za';
function getOrderTotal($conn, $payment_id)
{
// Prepare the SQL statement
$sql = "SELECT amount FROM payments WHERE payment_id = ?";
$stmt = $conn->prepare($sql);
if ($stmt) {
// Bind the parameter
$stmt->bind_param("s", $payment_id); // Assuming order_id is a string (UUID)
// Execute the statement
$stmt->execute();
// Get the result
$result = $stmt->get_result();
// Fetch the order total
if ($row = $result->fetch_assoc()) {
return $row['amount'];
} else {
return 9999.00; // Order not found
}
// Close the statement
$stmt->close();
} else {
// Handle the error (you might want to log this or throw an exception)
return null;
}
}
function getOrderDesc($conn, $payment_id)
{
// Prepare the SQL statement
$sql = "SELECT description FROM payments WHERE payment_id = ?";
$stmt = $conn->prepare($sql);
if ($stmt) {
// Bind the parameter
$stmt->bind_param("s", $payment_id); // Assuming order_id is a string (UUID)
// Execute the statement
$stmt->execute();
// Get the result
$result = $stmt->get_result();
// Fetch the order total
if ($row = $result->fetch_assoc()) {
return $row['description'];
} else {
return null; // Order not found
}
// Close the statement
$stmt->close();
} else {
// Handle the error (you might want to log this or throw an exception)
return null;
}
}
function getUserIdByPaymentId($payment_id, $conn)
{
// Prepare the SQL query to fetch user_id from payments table
$query = "SELECT user_id FROM payments WHERE payment_id = ?";
$user_id = "0";
// Prepare the statement
if ($stmt = $conn->prepare($query)) {
// Bind the payment_id parameter to the query
$stmt->bind_param("s", $payment_id);
// Execute the query
$stmt->execute();
// Bind the result to a variable
$stmt->bind_result($user_id);
// Fetch the result
if ($stmt->fetch()) {
// Return the user_id
return $user_id;
} else {
// Return null if no user is found
return null;
}
// Close the statement
$stmt->close();
} else {
// Handle query preparation failure
throw new Exception("Query preparation failed: " . $conn->error);
}
}
function setMemberStatus($user_id, $conn)
{
// Prepare the SQL query to update the member status
$query = "UPDATE users SET member = 1 WHERE user_id = ?";
// Prepare the statement
if ($stmt = $conn->prepare($query)) {
// Bind the user_id parameter to the query
$stmt->bind_param("i", $user_id);
// Execute the query
if ($stmt->execute()) {
// Check if any rows were affected
if ($stmt->affected_rows > 0) {
return true; // Success
} else {
return false; // No rows updated, possibly no such user_id
}
} else {
// Handle query execution failure
throw new Exception("Failed to execute the query: " . $stmt->error);
}
// Close the statement
$stmt->close();
} else {
// Handle query preparation failure
throw new Exception("Query preparation failed: " . $conn->error);
}
}
function pfValidSignature($pfData, $pfParamString, $pfPassphrase = 'SheSells7Shells')
{
$tempParamString = $pfPassphrase === null ? $pfParamString : $pfParamString . '&passphrase=' . urlencode($pfPassphrase);
$signature = md5($tempParamString);
return ($pfData['signature'] === $signature);
}
function pfValidIP()
{
$validHosts = [
'www.payfast.co.za',
'sandbox.payfast.co.za',
'w1w.payfast.co.za',
'w2w.payfast.co.za',
];
$validIps = [];
foreach ($validHosts as $pfHostname) {
$ips = gethostbynamel($pfHostname);
if ($ips !== false) {
$validIps = array_merge($validIps, $ips);
}
}
$validIps = array_unique($validIps);
$referrerIp = gethostbyname(parse_url($_SERVER['HTTP_REFERER'])['host']);
return in_array($referrerIp, $validIps, true);
}
function pfValidPaymentData($cartTotal, $pfData)
{
return !(abs((float)$cartTotal - (float)$pfData['amount_gross']) > 0.01);
}
function pfValidServerConfirmation($pfParamString, $pfHost, $pfProxy = null)
{
if (in_array('curl', get_loaded_extensions(), true)) {
$url = 'https://' . $pfHost . '/eng/query/validate';
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, NULL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $pfParamString);
if (!empty($pfProxy)) curl_setopt($ch, CURLOPT_PROXY, $pfProxy);
$response = curl_exec($ch);
curl_close($ch);
return $response === 'VALID';
}
return false;
}
// Tell Payfast that this page is reachable by triggering a header 200
header('HTTP/1.0 200 OK');
flush();
$dbhost = "localhost";
$dbuser = "aqmqeocm_4wdcsa";
$dbpass = "Toxicbuny1!";
$dbname = "aqmqeocm_4wdcsa";
if (!$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname)) {
die("Failed to connect: " . mysqli_connect_error());
}
define('SANDBOX_MODE', true);
$pfHost = SANDBOX_MODE ? 'sandbox.payfast.co.za' : 'www.payfast.co.za';
// Posted variables from ITN
$pfData = $_POST;
$payment_id = $pfData['m_payment_id'];
$pfstatus = $pfData['payment_status'];
$orderTotal = getOrderTotal($conn, $payment_id);
$description = getOrderDesc($conn, $payment_id);
$user_id = getUserIdByPaymentId($payment_id, $conn);
// Strip any slashes in data
foreach ($pfData as $key => $val) {
$pfData[$key] = stripslashes($val);
}
// Convert posted variables to a string
$pfParamString = '';
foreach ($pfData as $key => $val) {
if ($key !== 'signature') {
$pfParamString .= $key . '=' . urlencode($val) . '&';
} else {
break;
}
}
$pfParamString = rtrim($pfParamString, '&');
// Initialize check results
$checkResults = [];
// Perform checks
if (!pfValidSignature($pfData, $pfParamString)) {
$checkResults[] = "Signature check failed.";
}
if (!pfValidIP()) {
$checkResults[] = "IP check failed.";
}
if (!pfValidPaymentData($orderTotal, $pfData)) {
$checkResults[] = "Payment data check failed. order= " . $payment_id . " 4WDCSA_Total=" . $orderTotal . " PFtotal=" . $pfData['amount_gross'];
}
if (!pfValidServerConfirmation($pfParamString, $pfHost)) {
$checkResults[] = "Server confirmation check failed.";
}
// Log results to the file
$myfile = fopen($payment_id . ".txt", "w") or die("Unable to open file!");
if (empty($checkResults)) {
fwrite($myfile, $pfstatus . "\n");
fwrite($myfile, $payment_id . " passed all checks.\n");
// Update the database
$conn->begin_transaction();
try {
// Update payments table
$stmt = $conn->prepare("UPDATE payments SET status = ? WHERE payment_id = ?");
$status = "PAID"; // Explicitly set the status to "PAID"
$stmt->bind_param("ss", $status, $payment_id);
if (!$stmt->execute()) {
throw new Exception("Failed to update payments table.");
}
$stmt = $conn->prepare("UPDATE bookings SET status = ? WHERE payment_id = ?");
if ($stmt) {
$status = "PAID"; // Explicitly set the status to "PAID"
$stmt->bind_param("ss", $status, $payment_id);
if (!$stmt->execute()) {
throw new Exception("Failed to update bookings table.");
}
$stmt->close();
} else {
throw new Exception("Failed to prepare statement for bookings table: " . $conn->error);
}
setMemberStatus($user_id, $conn);
// Commit transaction
$conn->commit();
fwrite($myfile, "Database updated successfully.\n");
} catch (Exception $e) {
// Rollback transaction in case of error
$conn->rollback();
fwrite($myfile, "Database update failed: " . $e->getMessage() . "\n");
}
$stmt->close();
} else {
fwrite($myfile, $pfstatus . "\n");
foreach ($checkResults as $result) {
fwrite($myfile, $result . "\n");
}
$conn->begin_transaction();
try {
// Update payments table with 'FAILED CHECKS' status
$stmt = $conn->prepare("UPDATE payments SET status = 'FAILED CHECKS' WHERE payment_id = ?");
$stmt->bind_param("i", $payment_id);
if (!$stmt->execute()) {
throw new Exception("Failed to update payments table.");
}
// Commit transaction
$conn->commit();
fwrite($myfile, "Database updated successfully.\n");
} catch (Exception $e) {
// Rollback transaction in case of error
$conn->rollback();
fwrite($myfile, "Database update failed: " . $e->getMessage() . "\n");
}
$stmt->close();
}
fclose($myfile);
$conn->close();

336
src/pages/shop/confirm2.php Normal file
View File

@@ -0,0 +1,336 @@
<?php
// $pfHost = 'www.payfast.co.za';
function getOrderTotal($conn, $payment_id)
{
// Prepare the SQL statement
$sql = "SELECT amount FROM payments WHERE payment_id = ?";
$stmt = $conn->prepare($sql);
if ($stmt) {
// Bind the parameter
$stmt->bind_param("s", $payment_id); // Assuming order_id is a string (UUID)
// Execute the statement
$stmt->execute();
// Get the result
$result = $stmt->get_result();
// Fetch the order total
if ($row = $result->fetch_assoc()) {
return $row['amount'];
} else {
return 9999.00; // Order not found
}
// Close the statement
$stmt->close();
} else {
// Handle the error (you might want to log this or throw an exception)
return null;
}
}
function getOrderDesc($conn, $payment_id)
{
// Prepare the SQL statement
$sql = "SELECT description FROM payments WHERE payment_id = ?";
$stmt = $conn->prepare($sql);
if ($stmt) {
// Bind the parameter
$stmt->bind_param("s", $payment_id); // Assuming order_id is a string (UUID)
// Execute the statement
$stmt->execute();
// Get the result
$result = $stmt->get_result();
// Fetch the order total
if ($row = $result->fetch_assoc()) {
return $row['description'];
} else {
return null; // Order not found
}
// Close the statement
$stmt->close();
} else {
// Handle the error (you might want to log this or throw an exception)
return null;
}
}
function getUserIdByPaymentId($payment_id, $conn)
{
// Prepare the SQL query to fetch user_id from payments table
$query = "SELECT user_id FROM payments WHERE payment_id = ?";
$user_id = "0";
// Prepare the statement
if ($stmt = $conn->prepare($query)) {
// Bind the payment_id parameter to the query
$stmt->bind_param("s", $payment_id);
// Execute the query
$stmt->execute();
// Bind the result to a variable
$stmt->bind_result($user_id);
// Fetch the result
if ($stmt->fetch()) {
// Return the user_id
return $user_id;
} else {
// Return null if no user is found
return null;
}
// Close the statement
$stmt->close();
} else {
// Handle query preparation failure
throw new Exception("Query preparation failed: " . $conn->error);
}
}
function setMemberStatus($user_id, $conn)
{
// Prepare the SQL query to update the member status
$query = "UPDATE users SET member = 1 WHERE user_id = ?";
// Prepare the statement
if ($stmt = $conn->prepare($query)) {
// Bind the user_id parameter to the query
$stmt->bind_param("i", $user_id);
// Execute the query
if ($stmt->execute()) {
// Check if any rows were affected
if ($stmt->affected_rows > 0) {
return true; // Success
} else {
return false; // No rows updated, possibly no such user_id
}
} else {
// Handle query execution failure
throw new Exception("Failed to execute the query: " . $stmt->error);
}
// Close the statement
$stmt->close();
} else {
// Handle query preparation failure
throw new Exception("Query preparation failed: " . $conn->error);
}
}
function pfValidSignature($pfData, $pfParamString, $pfPassphrase = 'SheSells7Shells')
{
$tempParamString = $pfPassphrase === null ? $pfParamString : $pfParamString . '&passphrase=' . urlencode($pfPassphrase);
$signature = md5($tempParamString);
return ($pfData['signature'] === $signature);
}
function pfValidIP()
{
$validHosts = [
'www.payfast.co.za',
'sandbox.payfast.co.za',
'w1w.payfast.co.za',
'w2w.payfast.co.za',
];
$validIps = [];
foreach ($validHosts as $pfHostname) {
$ips = gethostbynamel($pfHostname);
if ($ips !== false) {
$validIps = array_merge($validIps, $ips);
}
}
$validIps = array_unique($validIps);
$referrerIp = gethostbyname(parse_url($_SERVER['HTTP_REFERER'])['host']);
return in_array($referrerIp, $validIps, true);
}
function pfValidPaymentData($cartTotal, $pfData)
{
return !(abs((float)$cartTotal - (float)$pfData['amount_gross']) > 0.01);
}
function pfValidServerConfirmation($pfParamString, $pfHost, $pfProxy = null)
{
if (in_array('curl', get_loaded_extensions(), true)) {
$url = 'https://' . $pfHost . '/eng/query/validate';
$ch = curl_init();
curl_setopt($ch, CURLOPT_USERAGENT, NULL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $pfParamString);
if (!empty($pfProxy)) curl_setopt($ch, CURLOPT_PROXY, $pfProxy);
$response = curl_exec($ch);
curl_close($ch);
return $response === 'VALID';
}
return false;
}
// Tell Payfast that this page is reachable by triggering a header 200
header('HTTP/1.0 200 OK');
flush();
$dbhost = "localhost";
$dbuser = "aqmqeocm_4wdcsa";
$dbpass = "Toxicbuny1!";
$dbname = "aqmqeocm_4wdcsa";
if (!$conn = mysqli_connect($dbhost, $dbuser, $dbpass, $dbname)) {
die("Failed to connect: " . mysqli_connect_error());
}
define('SANDBOX_MODE', true);
$pfHost = SANDBOX_MODE ? 'sandbox.payfast.co.za' : 'www.payfast.co.za';
// Posted variables from ITN
$pfData = $_POST;
$payment_id = $pfData['m_payment_id'];
$pfstatus = $pfData['payment_status'];
$orderTotal = getOrderTotal($conn, $payment_id);
$description = getOrderDesc($conn, $payment_id);
$user_id = getUserIdByPaymentId($payment_id, $conn);
// Strip any slashes in data
foreach ($pfData as $key => $val) {
$pfData[$key] = stripslashes($val);
}
// Convert posted variables to a string
$pfParamString = '';
foreach ($pfData as $key => $val) {
if ($key !== 'signature') {
$pfParamString .= $key . '=' . urlencode($val) . '&';
} else {
break;
}
}
$pfParamString = rtrim($pfParamString, '&');
// Initialize check results
$checkResults = [];
// Perform checks
if (!pfValidSignature($pfData, $pfParamString)) {
$checkResults[] = "Signature check failed.";
}
if (!pfValidIP()) {
$checkResults[] = "IP check failed.";
}
if (!pfValidPaymentData($orderTotal, $pfData)) {
$checkResults[] = "Payment data check failed. order= " . $payment_id . " 4WDCSA_Total=" . $orderTotal . " PFtotal=" . $pfData['amount_gross'];
}
if (!pfValidServerConfirmation($pfParamString, $pfHost)) {
$checkResults[] = "Server confirmation check failed.";
}
// Log results to the file
$myfile = fopen($payment_id . ".txt", "w") or die("Unable to open file!");
if (empty($checkResults)) {
fwrite($myfile, "Starting database update process for payment ID: $payment_id\n");
fwrite($myfile, "Payment status: $pfstatus\n");
// Begin database transaction
$conn->begin_transaction();
fwrite($myfile, "Transaction started.\n");
try {
// Step 1: Update payments table
fwrite($myfile, "Preparing to update payments table...\n");
$stmt = $conn->prepare("UPDATE payments SET status = ? WHERE payment_id = ?");
if (!$stmt) {
throw new Exception("Failed to prepare statement for payments table: " . $conn->error);
}
fwrite($myfile, "Prepared statement for payments table.\n");
$status = "PAID"; // Explicitly set the status to "PAID"
$stmt->bind_param("ss", $status, $payment_id);
fwrite($myfile, "Bound parameters for payments table update.\n");
if (!$stmt->execute()) {
throw new Exception("Failed to execute update for payments table: " . $stmt->error);
}
fwrite($myfile, "Payments table updated successfully.\n");
$stmt->close();
fwrite($myfile, "Closed statement for payments table update.\n");
// Step 2: Update membership_fees table
fwrite($myfile, "Preparing to update membership_fees table...\n");
$stmt = $conn->prepare("UPDATE membership_fees SET payment_status = ? WHERE payment_id = ?");
if (!$stmt) {
throw new Exception("Failed to prepare statement for membership_fees table: " . $conn->error);
}
fwrite($myfile, "Prepared statement for membership_fees table.\n");
$status = "PAID"; // Explicitly set the status to "PAID"
$stmt->bind_param("ss", $status, $payment_id);
fwrite($myfile, "Bound parameters for membership_fees table update.\n");
if (!$stmt->execute()) {
throw new Exception("Failed to execute update for membership_fees table: " . $stmt->error);
}
fwrite($myfile, "Membership_fees table updated successfully.\n");
$stmt->close();
fwrite($myfile, "Closed statement for membership_fees table update.\n");
// Step 3: Set member status
fwrite($myfile, "Calling setMemberStatus()...\n");
setMemberStatus($user_id, $conn);
fwrite($myfile, "setMemberStatus() executed successfully.\n");
// Commit transaction
$conn->commit();
fwrite($myfile, "Transaction committed successfully. Database updates complete.\n");
} catch (Exception $e) {
// Rollback transaction in case of error
$conn->rollback();
fwrite($myfile, "Transaction rolled back due to error: " . $e->getMessage() . "\n");
}
} else {
fwrite($myfile, $pfstatus . "\n");
foreach ($checkResults as $result) {
fwrite($myfile, $result . "\n");
}
$conn->begin_transaction();
try {
// Update payments table with 'FAILED CHECKS' status
$stmt = $conn->prepare("UPDATE payments SET status = 'FAILED CHECKS' WHERE payment_id = ?");
$stmt->bind_param("i", $payment_id);
if (!$stmt->execute()) {
throw new Exception("Failed to update payments table.");
}
// Commit transaction
$conn->commit();
fwrite($myfile, "Database updated successfully.\n");
} catch (Exception $e) {
// Rollback transaction in case of error
$conn->rollback();
fwrite($myfile, "Database update failed: " . $e->getMessage() . "\n");
}
$stmt->close();
}
fclose($myfile);
$conn->close();

View File

@@ -0,0 +1,150 @@
<?php
$headerStyle = 'light';
include_once(dirname(dirname(dirname(__DIR__))) . '/header.php');
checkUserSession();
$user_id = $_SESSION['user_id'];
if (!isset($_GET['token']) || empty($_GET['token'])) {
die("Invalid request.");
}
$token = $_GET['token'];
// echo $token;
$booking_id = decryptData($token, $salt);
// Retrieve booking details from the database
$sql = "SELECT eft_id, total_amount, discount_amount, trip_id, course_id FROM bookings WHERE booking_id = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
die("Prepare failed: " . $conn->error);
}
$stmt->bind_param("i", $booking_id);
$stmt->execute();
$stmt->bind_result($eft_id, $amount, $discount, $trip_id, $course_id);
$stmt->fetch();
$stmt->close();
// Check if booking was found
if (!$eft_id) {
die("Error: Booking not found.");
}
// Retrieve trip details
$sql = "SELECT trip_name, start_date, end_date FROM trips WHERE trip_id = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
die("Prepare failed: " . $conn->error);
}
$stmt->bind_param("i", $trip_id);
$stmt->execute();
$stmt->bind_result($trip_name, $start_date, $end_date);
$stmt->fetch();
$stmt->close();
// Retrieve trip details
$sql = "SELECT course_type, date FROM courses WHERE course_id = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
die("Prepare failed: " . $conn->error);
}
$stmt->bind_param("i", $course_id);
$stmt->execute();
$stmt->bind_result($type, $start_date);
$stmt->fetch();
$stmt->close();
if ($type === "driver_training") {
$course_name = "Basic 4X4 Driver Training Course";
} elseif ($type === "bush_mechanics") {
$course_name = "Bush Mechanics Course";
} elseif ($type === "rescue_recovery") {
$course_name = "Rescue & Recovery Training Course";
} else {
$course_name = "General Course"; // Default fallback description
}
// $start_date = $date;
// Get user's email
$sql = "SELECT email FROM users WHERE user_id = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) {
die("Prepare failed: " . $conn->error);
}
$stmt->bind_param("i", $user_id);
$stmt->execute();
$stmt->bind_result($user_email);
$stmt->fetch();
$stmt->close();
$conn->close();
$payment_amount = $amount - $discount;
?>
<?php
$bannerFolder = 'assets/images/banners/';
$bannerImages = glob($bannerFolder . '*.{jpg,jpeg,png,webp}', GLOB_BRACE);
$randomBanner = 'assets/images/base4/camping.jpg'; // default fallback
if (!empty($bannerImages)) {
$randomBanner = $bannerImages[array_rand($bannerImages)];
}
?>
<section class="page-banner-area pt-50 pb-35 rel z-1 bgs-cover" style="background-image: url('<?php echo $randomBanner; ?>');">
<div class="banner-overlay"></div>
<div class="container">
<div class="banner-inner text-white mb-50">
<h2 class="page-title mb-10" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">Payment</h2>
<nav aria-label="breadcrumb">
<ol class="breadcrumb justify-content-center mb-20" data-aos="fade-right" data-aos-delay="200" data-aos-duration="1500" data-aos-offset="50">
<li class="breadcrumb-item"><a href="index">Home</a></li>
<li class="breadcrumb-item">Booking</li>
<li class="breadcrumb-item active">Payment</li>
</ol>
</nav>
</div>
</div>
</section>
<!-- About Us Area start -->
<section class="about-us-area pt-90 pb-100 rel z-1">
<div class="container">
<div class="row gap-100 align-items-center">
<div class="col-lg-12">
<div class="destination-details-content rmb-55" data-aos="fade-left" data-aos-duration="1500" data-aos-offset="50">
<div class="section-title mb-25">
<span class="h2 mb-15">Booking Summary:</span>
<h2><?php
if ($trip_name != NULL){
echo htmlspecialchars($trip_name).'</h2><h3>'. htmlspecialchars($start_date).' - '.htmlspecialchars($end_date).'</h3>';
} else {
echo htmlspecialchars($course_name).'</h2><h3>'. htmlspecialchars($start_date).'</h3>';
}?>
</div>
<p>Your invoice has been sent to <b><?php echo htmlspecialchars($user_email); ?></b>. Please upload your proof of payment below.</p>
<!-- <p>Bookings not paid for within 24 hours will be forfeited.</p> -->
<h5>Payment Details:</h5>
<p>The Four Wheel Drive Club of Southern Africa<br>FNB<br>Account Number: 58810022334<br>Branch code: 250655<br>Reference: <?php echo htmlspecialchars($eft_id); ?><br>Amount: R <?php echo number_format($payment_amount, 2); ?></p>
<a href="submit_pop" class="theme-btn style-two style-three" style="width:100%;">
<span data-hover="Submit Proof of Payment">Submit Proof of Payment</span>
<i class="fal fa-arrow-right"></i>
</a>
</div>
</div>
</div>
</div>
</section>
<!-- About Us Area end -->
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>

View File

@@ -0,0 +1,12 @@
<?php
session_start();
// Check if the cart exists in the session
if (isset($_SESSION['cart']) && !empty($_SESSION['cart'])) {
echo '<pre>';
print_r($_SESSION['cart']); // Display cart contents in a readable format
echo '</pre>';
} else {
echo 'Cart is empty.';
}
?>