34968-vm/order_status.php
Flatlogic Bot acff14d6dc V24
2025-10-16 20:45:21 +00:00

269 lines
9.9 KiB
PHP

<?php
session_start();
require_once 'db/config.php';
// Allow guest access with a token
$order_id = $_GET['order_id'] ?? null;
$token = $_GET['token'] ?? null;
$user_id = $_SESSION['user_id'] ?? null;
if (!$order_id) {
header("Location: index.php");
exit();
}
$pdo = db();
$order = null;
// Fetch order details along with restaurant location
$sql = "SELECT o.*, r.name as restaurant_name, r.lat as restaurant_lat, r.lng as restaurant_lng FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ?";
$params = [$order_id];
if ($user_id) {
$sql .= " AND o.user_id = ?";
$params[] = $user_id;
} elseif ($token) {
$sql .= " AND o.token = ?";
$params[] = $token;
} else {
// No user and no token, deny access
include 'header.php';
echo "<div class='container mt-5'><div class='alert alert-danger'>Authentication required to view this order.</div></div>";
include 'footer.php';
exit();
}
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$order = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$order) {
include 'header.php';
echo "<div class='container mt-5'><div class='alert alert-danger'>Order not found or you do not have permission to view it.</div></div>";
include 'footer.php';
exit();
}
// Fetch order items
$stmt = $pdo->prepare("SELECT oi.*, mi.name as item_name FROM order_items oi JOIN menu_items mi ON oi.menu_item_id = mi.id WHERE oi.order_id = ?");
$stmt->execute([$order_id]);
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
include 'header.php';
?>
<div class="container mt-5">
<div class="row">
<div class="col-lg-8 offset-lg-2">
<div class="card shadow-sm mb-4">
<div class="card-body text-center">
<h1 class="card-title fw-bold">Thank You For Your Order!</h1>
<p class="text-muted">Order #<?php echo $order['id']; ?></p>
<p><strong>Restaurant:</strong> <?php echo htmlspecialchars($order['restaurant_name']); ?></p>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-body">
<h4 class="card-title text-center mb-4">Live Order Tracking</h4>
<div id="map" style="height: 400px; border-radius: .25rem;"></div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body">
<h4 class="card-title text-center mb-4">Order Status</h4>
<div id="order-status-timeline">
<!-- Timeline will be dynamically generated by JavaScript -->
</div>
</div>
</div>
<div class="card shadow-sm mt-4">
<div class="card-body">
<h5 class="card-title">Order Summary</h5>
<ul class="list-group list-group-flush">
<?php foreach ($items as $item): ?>
<li class="list-group-item d-flex justify-content-between align-items-center">
<?php echo htmlspecialchars($item['item_name']); ?> (x<?php echo $item['quantity']; ?>)
<span>$<?php echo number_format($item['price'] * $item['quantity'], 2); ?></span>
</li>
<?php endforeach; ?>
<li class="list-group-item d-flex justify-content-between align-items-center fw-bold">
Total
<span>$<?php echo number_format($order['total_price'], 2); ?></span>
</li>
</ul>
</div>
</div>
<div class="text-center mt-4">
<a href="index.php" class="btn btn-primary">Back to Home</a>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const orderId = <?php echo $order_id; ?>;
const token = '<?php echo $token; ?>';
const timelineContainer = document.getElementById('order-status-timeline');
const currentStatus = '<?php echo $order['status']; ?>';
const statuses = [
{ name: 'Pending', desc: 'Your order has been placed and is waiting for the restaurant to accept it.' },
{ name: 'Preparing', desc: 'The restaurant is preparing your food.' },
{ name: 'Out For Delivery', desc: 'A driver is on their way to you.' },
{ name: 'Delivered', desc: 'Your order has been delivered. Enjoy!' }
];
const cancelledStatus = { name: 'Cancelled', desc: 'This order has been cancelled.' };
function renderTimeline(status) {
timelineContainer.innerHTML = '';
let activeIndex = statuses.findIndex(s => s.name.toLowerCase() === status.toLowerCase());
if (status.toLowerCase() === 'cancelled') {
const item = document.createElement('div');
item.className = 'timeline-item timeline-cancelled';
item.innerHTML = `
<div class="timeline-icon"><i class="fas fa-times-circle"></i></div>
<div class="timeline-content">
<h5 class="fw-bold">${cancelledStatus.name}</h5>
<p class="text-muted">${cancelledStatus.desc}</p>
</div>
`;
timelineContainer.appendChild(item);
return;
}
statuses.forEach((s, index) => {
const item = document.createElement('div');
let itemClass = 'timeline-item';
let icon = '<i class="far fa-circle"></i>';
if (index < activeIndex) {
itemClass += ' timeline-complete';
icon = '<i class="fas fa-check-circle"></i>';
} else if (index === activeIndex) {
itemClass += ' timeline-active';
icon = '<i class="fas fa-dot-circle"></i>';
}
item.className = itemClass;
item.innerHTML = `
<div class="timeline-icon">${icon}</div>
<div class="timeline-content">
<h5 class="fw-bold">${s.name}</h5>
<p class="text-muted">${s.desc}</p>
</div>
`;
timelineContainer.appendChild(item);
});
}
function fetchStatus() {
let url = `api/get_order_status.php?order_id=${orderId}`;
if (token) {
url += `&token=${token}`;
}
fetch(url)
.then(response => response.json())
.then(data => {
if (data.status) {
renderTimeline(data.status);
} else if(data.error) {
console.error('Error fetching status:', data.error);
}
})
.catch(error => {
console.error('Fetch error:', error);
});
}
renderTimeline(currentStatus);
setInterval(fetchStatus, 10000); // Poll every 10 seconds
// --- New Map Logic ---
let map = null;
let driverMarker = null;
const restaurantLocation = {
lat: <?php echo $order['restaurant_lat'] ?? 'null'; ?>,
lng: <?php echo $order['restaurant_lng'] ?? 'null'; ?>
};
const driverIcon = L.icon({
iconUrl: 'assets/images/driver-icon.svg', // Custom driver icon
iconSize: [38, 38],
iconAnchor: [19, 38],
popupAnchor: [0, -38]
});
const restaurantIcon = L.icon({
iconUrl: 'assets/images/restaurant-icon.svg', // Custom restaurant icon
iconSize: [38, 38],
iconAnchor: [19, 38],
popupAnchor: [0, -38]
});
function initializeMap() {
if (map || !restaurantLocation.lat) return;
map = L.map('map').setView([restaurantLocation.lat, restaurantLocation.lng], 14);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
L.marker([restaurantLocation.lat, restaurantLocation.lng], { icon: restaurantIcon })
.addTo(map)
.bindPopup('<b><?php echo htmlspecialchars($order['restaurant_name']); ?></b>');
}
function fetchDriverLocation() {
// Only start fetching if the order is 'Out For Delivery'
if (currentStatus.toLowerCase() !== 'out for delivery') {
// You might want to hide the map or show a message until the driver is on the way
// For now, we just won't fetch the location.
return;
}
let url = `api/get_driver_location.php?order_id=${orderId}`;
if (token) {
url += `&token=${token}`;
}
fetch(url)
.then(response => response.json())
.then(data => {
if (data.lat && data.lng) {
const driverLatLng = [data.lat, data.lng];
if (!driverMarker) {
driverMarker = L.marker(driverLatLng, { icon: driverIcon }).addTo(map)
.bindPopup('<b>Your Driver</b>');
map.setView(driverLatLng, 15);
} else {
driverMarker.setLatLng(driverLatLng);
}
// Optional: Adjust map bounds to show both restaurant and driver
if (driverMarker) {
const bounds = L.latLngBounds([restaurantLocation.lat, restaurantLocation.lng], driverMarker.getLatLng());
map.fitBounds(bounds.pad(0.3));
}
} else {
console.warn('Driver location not available yet.');
}
})
.catch(error => console.error('Error fetching driver location:', error));
}
initializeMap();
// Start polling for driver location immediately and repeat every 5 seconds
fetchDriverLocation();
setInterval(fetchDriverLocation, 5000);
});
</script>
<?php include 'footer.php'; ?>