Compare commits
1 Commits
feature/tr
...
feature/ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb588d20ee |
@@ -37,6 +37,7 @@ RewriteRule ^member_info$ src/pages/memberships/member_info.php [L]
|
||||
RewriteRule ^bookings$ src/pages/bookings/bookings.php [L]
|
||||
RewriteRule ^campsites$ src/pages/bookings/campsites.php [L]
|
||||
RewriteRule ^campsite_booking$ src/pages/bookings/campsite_booking.php [L]
|
||||
RewriteRule ^add_campsite$ src/pages/add_campsite.php [L]
|
||||
RewriteRule ^trips$ src/pages/bookings/trips.php [L]
|
||||
RewriteRule ^trip-details$ src/pages/bookings/trip-details.php [L]
|
||||
RewriteRule ^course_details$ src/pages/bookings/course_details.php [L]
|
||||
@@ -81,7 +82,6 @@ RewriteRule ^admin_camp_bookings$ src/admin/admin_camp_bookings.php [L]
|
||||
RewriteRule ^admin_trip_bookings$ src/admin/admin_trip_bookings.php [L]
|
||||
RewriteRule ^admin_visitors$ src/admin/admin_visitors.php [L]
|
||||
RewriteRule ^admin_efts$ src/admin/admin_efts.php [L]
|
||||
RewriteRule ^add_campsite$ src/admin/add_campsite.php [L]
|
||||
RewriteRule ^admin_trips$ src/admin/admin_trips.php [L]
|
||||
RewriteRule ^manage_trips$ src/admin/manage_trips.php [L]
|
||||
|
||||
|
||||
BIN
assets/uploads/campsites/274d8e71982307bc5a699125966d5731.jpg
Normal file
BIN
assets/uploads/campsites/274d8e71982307bc5a699125966d5731.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
BIN
assets/uploads/campsites/3dd0636b3ed6926e10f0387a747d58c1.jpg
Normal file
BIN
assets/uploads/campsites/3dd0636b3ed6926e10f0387a747d58c1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/uploads/campsites/ae16ea8e89bb83dc3b85c54aa0e3fcec.jpg
Normal file
BIN
assets/uploads/campsites/ae16ea8e89bb83dc3b85c54aa0e3fcec.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 226 KiB |
BIN
assets/uploads/campsites/c613066cd83537a874355671e0213539.jpg
Normal file
BIN
assets/uploads/campsites/c613066cd83537a874355671e0213539.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/uploads/campsites/d21ae51aec635de07883d9586a1542df.jpg
Normal file
BIN
assets/uploads/campsites/d21ae51aec635de07883d9586a1542df.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
@@ -6,7 +6,16 @@ include_once('../config/functions.php');
|
||||
$conn = openDatabaseConnection();
|
||||
|
||||
$stmt = $conn->prepare("SELECT
|
||||
c.*,
|
||||
c.id,
|
||||
c.name,
|
||||
c.description,
|
||||
c.website,
|
||||
c.telephone,
|
||||
c.latitude,
|
||||
c.longitude,
|
||||
c.thumbnail,
|
||||
c.country,
|
||||
c.province,
|
||||
u.first_name,
|
||||
u.last_name,
|
||||
u.profile_pic
|
||||
@@ -26,6 +35,8 @@ while ($row = $result->fetch_assoc()) {
|
||||
'latitude' => $row['latitude'],
|
||||
'longitude' => $row['longitude'],
|
||||
'thumbnail' => $row['thumbnail'],
|
||||
'country' => $row['country'],
|
||||
'province' => $row['province'],
|
||||
'user' => [
|
||||
'first_name' => $row['first_name'],
|
||||
'last_name' => $row['last_name'],
|
||||
|
||||
118
src/pages/add_campsite.php
Normal file
118
src/pages/add_campsite.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
$rootPath = dirname(dirname(__DIR__));
|
||||
require_once($rootPath . '/src/config/env.php');
|
||||
include_once($rootPath . '/src/config/connection.php');
|
||||
include_once($rootPath . '/src/config/functions.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') : '';
|
||||
$country = isset($_POST['country']) ? htmlspecialchars($_POST['country'], ENT_QUOTES, 'UTF-8') : '';
|
||||
$province = isset($_POST['province']) ? htmlspecialchars($_POST['province'], 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 = $rootPath . "/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 = "assets/uploads/campsites/" . $randomFilename;
|
||||
} else {
|
||||
http_response_code(500);
|
||||
die('Failed to move uploaded file.');
|
||||
}
|
||||
}
|
||||
|
||||
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
|
||||
|
||||
if ($id > 0) {
|
||||
// Verify ownership - check if the campsite belongs to the current user
|
||||
$ownerCheckStmt = $conn->prepare("SELECT user_id FROM campsites WHERE id = ?");
|
||||
$ownerCheckStmt->bind_param("i", $id);
|
||||
$ownerCheckStmt->execute();
|
||||
$ownerResult = $ownerCheckStmt->get_result();
|
||||
|
||||
if ($ownerResult->num_rows === 0) {
|
||||
http_response_code(404);
|
||||
die('Campsite not found.');
|
||||
}
|
||||
|
||||
$ownerRow = $ownerResult->fetch_assoc();
|
||||
if ($ownerRow['user_id'] != $user_id) {
|
||||
http_response_code(403);
|
||||
die('You do not have permission to edit this campsite. Only the owner can make changes.');
|
||||
}
|
||||
|
||||
$ownerCheckStmt->close();
|
||||
|
||||
// UPDATE
|
||||
if ($thumbnailPath) {
|
||||
$stmt = $conn->prepare("UPDATE campsites SET name=?, description=?, country=?, province=?, latitude=?, longitude=?, website=?, telephone=?, thumbnail=? WHERE id=?");
|
||||
$stmt->bind_param("ssssddsssi", $name, $desc, $country, $province, $lat, $lng, $website, $telephone, $thumbnailPath, $id);
|
||||
} else {
|
||||
$stmt = $conn->prepare("UPDATE campsites SET name=?, description=?, country=?, province=?, latitude=?, longitude=?, website=?, telephone=? WHERE id=?");
|
||||
$stmt->bind_param("ssssddssi", $name, $desc, $country, $province, $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, country, province, latitude, longitude, website, telephone, thumbnail, user_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->bind_param("ssssddsssi", $name, $desc, $country, $province, $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");
|
||||
?>
|
||||
@@ -25,74 +25,303 @@ while ($row = $result->fetch_assoc()) {
|
||||
|
||||
.info-box img {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
|
||||
}
|
||||
|
||||
/* Form styling to match manage_trips */
|
||||
.campsite-form-container {
|
||||
background: #f9f9f7;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 10px;
|
||||
padding: 30px;
|
||||
margin: 20px 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.campsite-form-container h5 {
|
||||
color: #2c3e50;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.campsite-form-container .form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.campsite-form-container label {
|
||||
font-weight: 500;
|
||||
color: #34495e;
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.campsite-form-container .form-control {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.campsite-form-container .form-control:focus {
|
||||
border-color: #4CAF50;
|
||||
box-shadow: 0 0 0 0.2rem rgba(76, 175, 80, 0.25);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.campsite-form-container .form-control select {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.campsite-form-container .btn {
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
/* Table styling to match admin trips */
|
||||
.campsites-table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.campsites-table thead th {
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.campsites-table thead th::after {
|
||||
content: '\25B2';
|
||||
font-size: 0.8em;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.campsites-table thead th.asc::after {
|
||||
content: '\25B2';
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.campsites-table thead th.desc::after {
|
||||
content: '\25BC';
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.campsites-table tbody tr:nth-child(odd) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.campsites-table tbody tr:nth-child(even) {
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.campsites-table tbody td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.campsites-table tbody tr:nth-child(even) td:first-child {
|
||||
border-top-left-radius: 10px;
|
||||
border-bottom-left-radius: 10px;
|
||||
}
|
||||
|
||||
.campsites-table tbody tr:nth-child(even) td:last-child {
|
||||
border-top-right-radius: 10px;
|
||||
border-bottom-right-radius: 10px;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 25px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.campsite-group {
|
||||
color: #484848;
|
||||
background: #f9f9f7;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 10px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<?php
|
||||
$pageTitle = 'Campsites';
|
||||
$breadcrumbs = [['Home' => 'index.php']];
|
||||
require_once($rootPath . '/components/banner.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">
|
||||
<section class="tour-list-page py-100 rel">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||
<h3>Campsites Map</h3>
|
||||
<button class="theme-btn" id="toggleFormBtn" onclick="toggleCampsiteForm()">
|
||||
<i class="far fa-plus"></i> Add Campsite
|
||||
</button>
|
||||
</div>
|
||||
<p style="color: #666; margin-bottom: 15px;">Click on the map to add a new campsite, or click on a marker to view details.</p>
|
||||
|
||||
<!-- Collapsible Campsite Form -->
|
||||
<div class="campsite-form-container" id="campsiteFormContainer">
|
||||
<h5>Add New Campsite</h5>
|
||||
<form id="addCampsiteForm" method="POST" action="add_campsite" enctype="multipart/form-data">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
|
||||
<input type="hidden" name="latitude" id="latitude">
|
||||
<input type="hidden" name="longitude" id="longitude">
|
||||
|
||||
<div class="row mt-35">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="campsite_name">Campsite Name *</label>
|
||||
<input type="text" id="campsite_name" class="form-control" name="name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="countrySelect">Country *</label>
|
||||
<select id="countrySelect" class="form-control" name="country" required>
|
||||
<option value="">-- Select Country --</option>
|
||||
<option value="South Africa">South Africa</option>
|
||||
<option value="Botswana">Botswana</option>
|
||||
<option value="Eswatini">Eswatini</option>
|
||||
<option value="Lesotho">Lesotho</option>
|
||||
<option value="Namibia">Namibia</option>
|
||||
<option value="Zimbabwe">Zimbabwe</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="provinceSelect">Province *</label>
|
||||
<select id="provinceSelect" class="form-control" name="province" required>
|
||||
<option value="">-- Select Province --</option>
|
||||
<option value="Eastern Cape">Eastern Cape</option>
|
||||
<option value="Free State">Free State</option>
|
||||
<option value="Gauteng">Gauteng</option>
|
||||
<option value="KwaZulu-Natal">KwaZulu-Natal</option>
|
||||
<option value="Limpopo">Limpopo</option>
|
||||
<option value="Mpumalanga">Mpumalanga</option>
|
||||
<option value="Northern Cape">Northern Cape</option>
|
||||
<option value="North West">North West</option>
|
||||
<option value="Western Cape">Western Cape</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label for="campsite_description">Description</label>
|
||||
<textarea id="campsite_description" class="form-control" name="description" rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="campsite_website">Booking URL</label>
|
||||
<input type="url" id="campsite_website" class="form-control" name="website">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="campsite_phone">Phone Number</label>
|
||||
<input type="text" id="campsite_phone" class="form-control" name="telephone">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="latitude_display">Latitude</label>
|
||||
<input type="text" id="latitude_display" class="form-control" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="longitude_display">Longitude</label>
|
||||
<input type="text" id="longitude_display" class="form-control" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group">
|
||||
<label for="campsite_thumbnail">Thumbnail Image</label>
|
||||
<input type="file" id="campsite_thumbnail" class="form-control" name="thumbnail" accept="image/*">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="form-group mb-0">
|
||||
<button class="theme-btn style-two" type="submit" style="width: 100%; margin-right: 10px;">Save Campsite</button>
|
||||
<button class="theme-btn" type="button" onclick="toggleCampsiteForm()" style="width: 100%; margin-top: 10px;">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="map" style="width: 100%; height: 500px;"></div>
|
||||
<!-- Add Campsite Modal -->
|
||||
|
||||
|
||||
<!-- Campsites Table -->
|
||||
<div style="margin-top: 40px;">
|
||||
<h4 style="margin-bottom: 20px;">All Campsites</h4>
|
||||
<input type="text" class="filter-input" id="campsitesFilter" placeholder="Filter results...">
|
||||
<div class="table-responsive">
|
||||
<table class="campsites-table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Website</th>
|
||||
<th>Phone</th>
|
||||
<th>Added By</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="campsitesTableBody">
|
||||
<!-- Populated by JavaScript -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</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 toggleCampsiteForm() {
|
||||
const container = document.getElementById("campsiteFormContainer");
|
||||
container.style.display = container.style.display === "none" ? "block" : "none";
|
||||
if (container.style.display === "block") {
|
||||
container.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
// Clear the form
|
||||
document.getElementById("addCampsiteForm").reset();
|
||||
// Remove the ID input if it exists
|
||||
let idInput = document.querySelector("#addCampsiteForm input[name='id']");
|
||||
if (idInput) {
|
||||
idInput.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function initMap() {
|
||||
map = new google.maps.Map(document.getElementById("map"), {
|
||||
center: {
|
||||
@@ -106,15 +335,19 @@ while ($row = $result->fetch_assoc()) {
|
||||
const lat = e.latLng.lat();
|
||||
const lng = e.latLng.lng();
|
||||
|
||||
resetForm();
|
||||
document.getElementById("latitude").value = lat;
|
||||
document.getElementById("longitude").value = lng;
|
||||
document.getElementById("latitude_display").value = lat.toFixed(6);
|
||||
document.getElementById("longitude_display").value = lng.toFixed(6);
|
||||
|
||||
const addModal = new bootstrap.Modal(document.getElementById("addCampsiteModal"));
|
||||
addModal.show();
|
||||
// Show the form container
|
||||
document.getElementById("campsiteFormContainer").style.display = "block";
|
||||
document.getElementById("campsiteFormContainer").scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
});
|
||||
|
||||
// Load existing campsites from PHP
|
||||
fetch("get_campsites.php")
|
||||
fetch("get_campsites")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.forEach(site => {
|
||||
@@ -156,21 +389,97 @@ while ($row = $result->fetch_assoc()) {
|
||||
infowindow.open(map, marker);
|
||||
});
|
||||
});
|
||||
|
||||
// Populate the table
|
||||
populateCampsitesTable(data);
|
||||
})
|
||||
.catch(err => console.error("Failed to load campsites:", err));
|
||||
}
|
||||
|
||||
function populateCampsitesTable(campsites) {
|
||||
const tableBody = document.getElementById("campsitesTableBody");
|
||||
tableBody.innerHTML = ""; // Clear existing rows
|
||||
|
||||
if (campsites.length === 0) {
|
||||
tableBody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted" style="padding: 30px;">
|
||||
No campsites added yet. Click on the map to add one!
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Group campsites by country and province
|
||||
const groupedByCountryAndProvince = {};
|
||||
campsites.forEach(site => {
|
||||
const country = site.country || 'Unknown Country';
|
||||
const province = site.province || 'Unknown Province';
|
||||
|
||||
if (!groupedByCountryAndProvince[country]) {
|
||||
groupedByCountryAndProvince[country] = {};
|
||||
}
|
||||
if (!groupedByCountryAndProvince[country][province]) {
|
||||
groupedByCountryAndProvince[country][province] = [];
|
||||
}
|
||||
groupedByCountryAndProvince[country][province].push(site);
|
||||
});
|
||||
|
||||
// Sort countries alphabetically
|
||||
const sortedCountries = Object.keys(groupedByCountryAndProvince).sort();
|
||||
|
||||
// Populate table with grouped data
|
||||
sortedCountries.forEach(country => {
|
||||
// Sort provinces alphabetically for this country
|
||||
const sortedProvinces = Object.keys(groupedByCountryAndProvince[country]).sort();
|
||||
|
||||
sortedProvinces.forEach(province => {
|
||||
// Add province group header
|
||||
const groupRow = document.createElement("tr");
|
||||
groupRow.innerHTML = `
|
||||
<td colspan="6" style="font-weight: 600; padding: 10px 8px; background-color: #f0f0f0;">
|
||||
<i class="fas fa-globe" style="color: #2196F3; margin-right: 8px;"></i>${country} - ${province}
|
||||
</td>
|
||||
`;
|
||||
tableBody.appendChild(groupRow);
|
||||
|
||||
// Add campsite rows for this province
|
||||
groupedByCountryAndProvince[country][province].forEach(site => {
|
||||
const row = document.createElement("tr");
|
||||
const userName = site.user && site.user.first_name
|
||||
? `${site.user.first_name} ${site.user.last_name}`
|
||||
: "Unknown";
|
||||
|
||||
row.innerHTML = `
|
||||
<td><strong>${site.name}</strong></td>
|
||||
<td>${site.description ? site.description.substring(0, 50) + (site.description.length > 50 ? '...' : '') : '-'}</td>
|
||||
<td>${site.website ? `<a href="${site.website}" target="_blank" class="link-primary">Visit</a>` : '-'}</td>
|
||||
<td>${site.telephone || '-'}</td>
|
||||
<td><small>${userName}</small></td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-warning" 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">Directions</a>
|
||||
</td>
|
||||
`;
|
||||
tableBody.appendChild(row);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function editCampsite(site) {
|
||||
// Pre-fill form
|
||||
document.querySelector("#addCampsiteForm input[name='name']").value = site.name;
|
||||
document.querySelector("#addCampsiteForm select[name='country']").value = site.country || '';
|
||||
document.querySelector("#addCampsiteForm select[name='province']").value = site.province || '';
|
||||
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;
|
||||
document.getElementById("latitude_display").value = parseFloat(site.latitude).toFixed(6);
|
||||
document.getElementById("longitude_display").value = parseFloat(site.longitude).toFixed(6);
|
||||
|
||||
// Add hidden ID input
|
||||
let idInput = document.querySelector("#addCampsiteForm input[name='id']");
|
||||
@@ -182,14 +491,13 @@ while ($row = $result->fetch_assoc()) {
|
||||
}
|
||||
idInput.value = site.id;
|
||||
|
||||
// Show the modal
|
||||
const addModal = new bootstrap.Modal(document.getElementById("addCampsiteModal"));
|
||||
addModal.show();
|
||||
// Show the form container
|
||||
document.getElementById("campsiteFormContainer").style.display = "block";
|
||||
document.getElementById("campsiteFormContainer").scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
</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'); ?>
|
||||
<?php include_once(dirname(dirname(dirname(__DIR__))) . '/components/insta_footer.php'); ?>
|
||||
Reference in New Issue
Block a user