feat: create events management admin system
- Add manage_events.php form for creating/editing events - Add process_event.php endpoint for CRUD operations with image uploads - Add admin_events.php list view with sorting, filtering, and delete functionality - Add database migration to add created_by, published, created_at, updated_at columns to events table - Add event images directory structure - All features follow same patterns as trip management system
This commit is contained in:
275
src/admin/admin_events.php
Normal file
275
src/admin/admin_events.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?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: 10px;
|
||||
}
|
||||
|
||||
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: 10px;
|
||||
font-size: 16px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
border-radius: 25px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.event-list {
|
||||
color: #484848;
|
||||
background: #f9f9f7;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 10px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.event-header {
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.event-header h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.action-buttons a, .action-buttons button {
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.badge-published {
|
||||
background-color: #28a745;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.badge-draft {
|
||||
background-color: #ffc107;
|
||||
color: black;
|
||||
}
|
||||
</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";
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function deleteEvent(eventId) {
|
||||
if (confirm('Are you sure you want to delete this event?')) {
|
||||
fetch('process_event?action=delete&event_id=' + eventId, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error: ' + data.message);
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error:', error));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
$pageTitle = 'Manage Events';
|
||||
$breadcrumbs = [['Home' => 'index'], [$pageTitle => '']];
|
||||
require_once($rootPath . '/components/banner.php');
|
||||
?>
|
||||
|
||||
<section class="tour-list-page py-10 rel z-1">
|
||||
<div class="container">
|
||||
<div class="row mb-20">
|
||||
<div class="col-lg-12">
|
||||
<a href="manage_events" class="theme-btn style-two">+ Create New Event</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Query to retrieve all events
|
||||
$stmt = $conn->prepare("SELECT event_id, name, type, location, date, published FROM events ORDER BY date DESC");
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
echo '<input type="text" class="filter-input" placeholder="Search events...">';
|
||||
echo '<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Event Name</th>
|
||||
<th>Type</th>
|
||||
<th>Location</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>';
|
||||
|
||||
while ($event = $result->fetch_assoc()) {
|
||||
$event_id = $event['event_id'];
|
||||
$name = htmlspecialchars($event['name']);
|
||||
$type = htmlspecialchars($event['type']);
|
||||
$location = htmlspecialchars($event['location']);
|
||||
$date = convertDate($event['date']);
|
||||
$status = $event['published'] ? '<span class="badge badge-published">Published</span>' : '<span class="badge badge-draft">Draft</span>';
|
||||
|
||||
echo "<tr>
|
||||
<td>{$name}</td>
|
||||
<td>{$type}</td>
|
||||
<td>{$location}</td>
|
||||
<td>{$date}</td>
|
||||
<td>{$status}</td>
|
||||
<td>
|
||||
<div class='action-buttons'>
|
||||
<a href='manage_events?event_id={$event_id}' class='btn-edit'>Edit</a>
|
||||
<button class='btn-delete' onclick='deleteEvent({$event_id})'>Delete</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>";
|
||||
}
|
||||
|
||||
echo '</tbody></table>';
|
||||
} else {
|
||||
echo '<div class="alert alert-info">No events found. <a href="manage_events">Create the first event</a></div>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php include_once($rootPath . '/components/insta_footer.php'); ?>
|
||||
Reference in New Issue
Block a user