V24
53
api/get_driver_location.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
if (!isset($_GET['order_id'])) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Order ID is required.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$order_id = $_GET['order_id'];
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
|
||||
// For guest users, we need a token
|
||||
$token = $_GET['token'] ?? null;
|
||||
|
||||
$pdoconn = db();
|
||||
|
||||
// Verify the user or guest has permission to view this order
|
||||
if ($user_id) {
|
||||
$stmt = $pdoconn->prepare("SELECT id FROM orders WHERE id = :order_id AND user_id = :user_id");
|
||||
$stmt->execute(['order_id' => $order_id, 'user_id' => $user_id]);
|
||||
} else if ($token) {
|
||||
$stmt = $pdoconn->prepare("SELECT id FROM orders WHERE id = :order_id AND token = :token");
|
||||
$stmt->execute(['order_id' => $order_id, 'token' => $token]);
|
||||
} else {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Authentication required.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($stmt->rowCount() == 0) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Order not found or access denied.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch driver location
|
||||
$stmt = $pdoconn->prepare("SELECT driver_lat, driver_lng FROM orders WHERE id = :order_id");
|
||||
$stmt->execute(['order_id' => $order_id]);
|
||||
$location = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$location || is_null($location['driver_lat']) || is_null($location['driver_lng'])) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Driver location not available yet.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
'lat' => $location['driver_lat'],
|
||||
'lng' => $location['driver_lng']
|
||||
]);
|
||||
@ -23,10 +23,7 @@ MajuroEats Theme
|
||||
/* --- Global Styles --- */
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
background-image: url('../assets/pasted-20251016-192041-2abf91d9.jpg');
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
background-color: var(--coconut-white);
|
||||
color: var(--text-dark);
|
||||
margin: 0;
|
||||
padding-top: 80px; /* Space for fixed header */
|
||||
@ -188,225 +185,174 @@ main {
|
||||
|
||||
/* --- Hero Section --- */
|
||||
.hero-section {
|
||||
background-image: url('../assets/pasted-20251016-192041-2abf91d9.jpg');
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
padding: 120px 0;
|
||||
background-color: #f8f9fa;
|
||||
padding: 100px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hero-content-container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
font-size: 3.8rem;
|
||||
.hero-title {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
margin: 0 0 15px;
|
||||
text-shadow: 0 3px 10px rgba(0,0,0,0.3);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.hero-content p {
|
||||
font-size: 1.3rem;
|
||||
margin: 0 auto 40px;
|
||||
opacity: 0.95;
|
||||
max-width: 600px;
|
||||
.hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
color: var(--text-light);
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.hero-search-form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 30px auto;
|
||||
box-shadow: var(--shadow-medium);
|
||||
.hero-section .btn-primary {
|
||||
background-color: #ff5a5f;
|
||||
border-color: #ff5a5f;
|
||||
font-size: 1.25rem;
|
||||
padding: 15px 40px;
|
||||
border-radius: 50px;
|
||||
overflow: hidden;
|
||||
max-width: 600px;
|
||||
background-color: var(--coconut-white);
|
||||
border: 3px solid var(--coconut-white);
|
||||
}
|
||||
|
||||
#address-input {
|
||||
flex-grow: 1;
|
||||
border: none;
|
||||
padding: 20px;
|
||||
font-size: 1.1rem;
|
||||
font-family: var(--font-family);
|
||||
color: var(--text-dark);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#address-input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#find-food-btn {
|
||||
background-color: var(--coral);
|
||||
color: var(--coconut-white);
|
||||
border: none;
|
||||
padding: 0 40px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
#find-food-btn:hover {
|
||||
background-color: var(--orange);
|
||||
.hero-section .btn-primary:hover {
|
||||
background-color: #e04f54;
|
||||
border-color: #e04f54;
|
||||
}
|
||||
|
||||
.delivery-note {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.8;
|
||||
.hero-section .text-muted {
|
||||
color: #6c757d !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* --- Promo Section --- */
|
||||
.promo-section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 30px;
|
||||
margin-bottom: 60px;
|
||||
/* --- How It Works Section --- */
|
||||
#how-it-works {
|
||||
padding: 60px 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.promo-card {
|
||||
background: var(--coconut-white);
|
||||
padding: 30px;
|
||||
border-radius: var(--border-radius);
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow-soft);
|
||||
transition: var(--transition);
|
||||
.step {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.promo-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: var(--shadow-medium);
|
||||
.step-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.promo-card h3 {
|
||||
.step h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--ocean-blue);
|
||||
margin: 0 0 10px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.promo-card p {
|
||||
font-size: 1rem;
|
||||
.step p {
|
||||
color: var(--text-light);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* --- Featured Restaurants --- */
|
||||
.featured-restaurants {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.restaurant-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.restaurant-card {
|
||||
background: var(--coconut-white);
|
||||
/* --- Restaurant & Cuisine Cards --- */
|
||||
.restaurant-card, .cuisine-card {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
overflow: hidden;
|
||||
transition: var(--transition);
|
||||
color: var(--text-dark);
|
||||
border: 2px solid transparent;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.restaurant-card:hover {
|
||||
transform: translateY(-10px);
|
||||
.restaurant-card:hover, .cuisine-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-medium);
|
||||
border-color: var(--coral);
|
||||
}
|
||||
|
||||
.card-image {
|
||||
.restaurant-card .card-img-top {
|
||||
border-top-left-radius: var(--border-radius);
|
||||
border-top-right-radius: var(--border-radius);
|
||||
height: 200px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 20px;
|
||||
.restaurant-card .card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.card-content h3 {
|
||||
font-size: 1.4rem;
|
||||
font-weight: 800;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
|
||||
.cuisine-tags {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-light);
|
||||
margin: 0 0 15px;
|
||||
}
|
||||
|
||||
.restaurant-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 1rem;
|
||||
.restaurant-card .card-title a {
|
||||
color: var(--text-dark);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rating-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.rating-display .star {
|
||||
color: #FFC700;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.delivery-info {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.see-all-container {
|
||||
text-align: center;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.see-all-btn {
|
||||
background-color: var(--coral);
|
||||
color: var(--coconut-white);
|
||||
padding: 15px 35px;
|
||||
border-radius: 50px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
border: 2px solid var(--coral);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.see-all-btn:hover {
|
||||
background-color: transparent;
|
||||
.restaurant-card .card-title a:hover {
|
||||
color: var(--coral);
|
||||
}
|
||||
|
||||
/* --- Footer --- */
|
||||
.main-footer {
|
||||
background-color: var(--text-dark);
|
||||
color: var(--sand);
|
||||
padding: 40px 0;
|
||||
.cuisine-card {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cuisine-card img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.cuisine-card h5 {
|
||||
color: var(--text-dark);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* --- Modal Styles --- */
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 10% auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
border-radius: var(--border-radius);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
color: #aaa;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-btn:hover,
|
||||
.close-btn:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
#confirm-location-btn {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* --- Responsive Design --- */
|
||||
@media (max-width: 992px) {
|
||||
.nav-links {
|
||||
@ -456,7 +402,7 @@ main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hero-content h1 {
|
||||
.hero-title {
|
||||
font-size: 2.8rem;
|
||||
}
|
||||
}
|
||||
@ -471,265 +417,10 @@ main {
|
||||
.nav-links {
|
||||
top: 70px;
|
||||
}
|
||||
.hero-content h1 {
|
||||
.hero-title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
.hero-content p {
|
||||
.hero-subtitle {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.location-actions {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* --- Empty State --- */
|
||||
.empty-state {
|
||||
padding: 80px 40px;
|
||||
background-color: var(--sand);
|
||||
border-radius: var(--border-radius);
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-light);
|
||||
max-width: 400px;
|
||||
margin: 10px auto 0;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 15px 35px;
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
border: 2px solid transparent;
|
||||
transition: var(--transition);
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--coral);
|
||||
color: var(--coconut-white);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: var(--orange);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-medium);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: var(--coconut-white);
|
||||
border-color: var(--coconut-white);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--coconut-white);
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
.location-actions {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1001;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background-color: #fefefe;
|
||||
margin: 10% auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #888;
|
||||
width: 80%;
|
||||
max-width: 600px;
|
||||
border-radius: var(--border-radius);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
color: #aaa;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close-button:hover,
|
||||
.close-button:focus {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#map {
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
#confirm-location {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
/* --- Back to Home Button --- */
|
||||
.back-to-home-btn {
|
||||
display: inline-block;
|
||||
margin-top: 10px;
|
||||
margin-bottom: -10px; /* to reduce space */
|
||||
padding: 8px 15px;
|
||||
background-color: #f8f9fa;
|
||||
color: var(--text-light);
|
||||
border-radius: 50px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 700;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.back-to-home-btn:hover {
|
||||
background-color: #e9ecef;
|
||||
color: var(--text-dark);
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
/* --- Featured Cuisines --- */
|
||||
.featured-cuisines {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.cuisine-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.cuisine-card {
|
||||
position: relative;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
transition: var(--transition);
|
||||
color: var(--coconut-white);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cuisine-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: var(--shadow-medium);
|
||||
}
|
||||
|
||||
.cuisine-card .card-image {
|
||||
height: 180px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.cuisine-card:hover .card-image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.cuisine-card-content {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8), transparent);
|
||||
}
|
||||
|
||||
.cuisine-card-content h3 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 5px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
/* --- Restaurants Page --- */
|
||||
.filter-bar {
|
||||
background-color: var(--sand);
|
||||
padding: 20px;
|
||||
border-radius: var(--border-radius);
|
||||
margin-bottom: 40px;
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.filter-bar .form-control, .filter-bar .form-select {
|
||||
border-radius: 50px;
|
||||
padding: 10px 20px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.filter-bar .btn-primary {
|
||||
background-color: var(--coral);
|
||||
border-color: var(--coral);
|
||||
border-radius: 50px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 700;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.restaurant-card .card-img-top {
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.restaurant-card .card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.restaurant-card .card-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 800;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.restaurant-card .card-text {
|
||||
color: var(--text-light);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.restaurant-card .btn-outline-primary {
|
||||
border-color: var(--coral);
|
||||
color: var(--coral);
|
||||
border-radius: 50px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.restaurant-card .btn-outline-primary:hover {
|
||||
background-color: var(--coral);
|
||||
color: var(--coconut-white);
|
||||
}
|
||||
|
||||
1
assets/images/driver-icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#007bff" width="38px" height="38px"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 274 B |
1
assets/images/restaurant-icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#dc3545" width="38px" height="38px"><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 274 B |
1
assets/images/step1.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="#ff5a5f" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
|
||||
|
After Width: | Height: | Size: 281 B |
1
assets/images/step2.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="#ff5a5f" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path><line x1="3" y1="6" x2="21" y2="6"></line><path d="M16 10a4 4 0 0 1-8 0"></path></svg>
|
||||
|
After Width: | Height: | Size: 332 B |
1
assets/images/step3.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="#ff5a5f" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 17.5V14H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v1.5"></path><path d="M18 8h1a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1h-1"></path><path d="M22 12l-3 3-3-3"></path><path d="M19 15V3"></path></svg>
|
||||
|
After Width: | Height: | Size: 383 B |
BIN
assets/pasted-20251016-201202-44c2bc42.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
assets/pasted-20251016-203923-9f036b50.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/pasted-20251016-204207-d203ea6e.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
103
hero.php
@ -1,25 +1,96 @@
|
||||
<section class="hero-section">
|
||||
<div class="hero-overlay"></div>
|
||||
<div class="container hero-content-container">
|
||||
<div class="hero-content">
|
||||
<h1>Your favorite local food, delivered.</h1>
|
||||
<p>Set your location to find restaurants near you.</p>
|
||||
<div class="location-actions">
|
||||
<button id="pin-location-btn" class="btn btn-primary">Pin a Location</button>
|
||||
<button id="use-location-btn" class="btn btn-secondary">Use My Location</button>
|
||||
</div>
|
||||
<a href="menu.php" class="find-restaurants-btn" style="display: none;"></a>
|
||||
<p class="delivery-note">MajuroEats delivers only within the main island zone (Rita–Laura).</p>
|
||||
<!-- hero.php -->
|
||||
<div class="hero-section">
|
||||
<div class="container text-center">
|
||||
<h1 class="hero-title">Your Favorite Food, Delivered</h1>
|
||||
<p class="hero-subtitle">Enter your location to see which restaurants deliver to you.</p>
|
||||
<a href="#" class="btn btn-primary btn-lg" id="pin-location-btn">Pin a Location on Map</a>
|
||||
<div class="mt-3">
|
||||
<a href="#" class="text-muted" id="use-location-btn">or use my current location</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Location Modal -->
|
||||
<div id="location-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<span class="close-btn">×</span>
|
||||
<h2>Pin Your Location</h2>
|
||||
<p>Click on the map to set your delivery address.</p>
|
||||
<h2>Pin Your Delivery Location</h2>
|
||||
<div id="map" style="height: 400px; width: 100%;"></div>
|
||||
<button id="confirm-location-btn" class="btn btn-primary w-100">Confirm Location</button>
|
||||
<button id="confirm-location-btn" class="btn btn-primary mt-3" disabled>Confirm Location</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const modal = document.getElementById('location-modal');
|
||||
const pinBtn = document.getElementById('pin-location-btn');
|
||||
const useLocationBtn = document.getElementById('use-location-btn');
|
||||
const closeBtn = document.querySelector('.close-btn');
|
||||
const confirmBtn = document.getElementById('confirm-location-btn');
|
||||
|
||||
let map;
|
||||
let marker;
|
||||
let selectedLocation = null;
|
||||
const majuroCenter = [7.11, 171.38];
|
||||
|
||||
function initMap(center, zoom) {
|
||||
if (map) {
|
||||
map.off();
|
||||
map.remove();
|
||||
}
|
||||
map = L.map('map').setView(center, zoom);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
}).addTo(map);
|
||||
|
||||
map.on('click', function(e) {
|
||||
if (marker) {
|
||||
map.removeLayer(marker);
|
||||
}
|
||||
marker = L.marker(e.latlng).addTo(map);
|
||||
selectedLocation = e.latlng;
|
||||
confirmBtn.disabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
pinBtn.onclick = function(e) {
|
||||
e.preventDefault();
|
||||
modal.style.display = 'block';
|
||||
setTimeout(() => initMap(majuroCenter, 13), 100);
|
||||
};
|
||||
|
||||
useLocationBtn.onclick = function(e) {
|
||||
e.preventDefault();
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(function(position) {
|
||||
const userLocation = {
|
||||
lat: position.coords.latitude,
|
||||
lng: position.coords.longitude
|
||||
};
|
||||
window.location.href = `restaurants.php?lat=${userLocation.lat}&lng=${userLocation.lng}`;
|
||||
}, function() {
|
||||
alert('Could not get your location. Please pin it on the map.');
|
||||
});
|
||||
} else {
|
||||
alert('Geolocation is not supported by this browser.');
|
||||
}
|
||||
};
|
||||
|
||||
closeBtn.onclick = function() {
|
||||
modal.style.display = 'none';
|
||||
};
|
||||
|
||||
confirmBtn.onclick = function() {
|
||||
if (selectedLocation) {
|
||||
window.location.href = `restaurants.php?lat=${selectedLocation.lat}&lng=${selectedLocation.lng}`;
|
||||
}
|
||||
modal.style.display = 'none';
|
||||
};
|
||||
|
||||
window.onclick = function(event) {
|
||||
if (event.target == modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
180
index.php
@ -1,132 +1,96 @@
|
||||
<?php
|
||||
// Handle AJAX request for nearby restaurants
|
||||
if (isset($_GET['action']) && $_GET['action'] == 'get_restaurants' && isset($_GET['lat']) && isset($_GET['lng'])) {
|
||||
require_once 'db/config.php';
|
||||
header('Content-Type: application/json');
|
||||
require_once 'header.php';
|
||||
require_once 'hero.php';
|
||||
require_once 'db/config.php';
|
||||
|
||||
$lat = (float)$_GET['lat'];
|
||||
$lng = (float)$_GET['lng'];
|
||||
$radius = 10; // Search radius in kilometers
|
||||
|
||||
$db = db();
|
||||
// Haversine formula to calculate distance
|
||||
$stmt = $db->prepare("
|
||||
SELECT r.id, r.name, r.image_url, AVG(rt.rating) as average_rating,
|
||||
(6371 * acos(cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + sin(radians(?)) * sin(radians(latitude)))) AS distance
|
||||
// Fetch top-rated restaurants
|
||||
$stmt = $pdo->query("
|
||||
SELECT r.*, AVG(ra.rating) as avg_rating
|
||||
FROM restaurants r
|
||||
LEFT JOIN ratings rt ON r.id = rt.restaurant_id
|
||||
WHERE latitude IS NOT NULL AND longitude IS NOT NULL
|
||||
LEFT JOIN ratings ra ON r.id = ra.restaurant_id
|
||||
GROUP BY r.id
|
||||
HAVING distance < ?
|
||||
ORDER BY distance
|
||||
LIMIT 12
|
||||
");
|
||||
ORDER BY avg_rating DESC
|
||||
LIMIT 6
|
||||
");
|
||||
$top_restaurants = $stmt->fetchAll();
|
||||
|
||||
$stmt->execute([$lat, $lng, $lat, $radius]);
|
||||
$restaurants = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Get cuisines for each restaurant
|
||||
$cuisine_sql = "SELECT c.name FROM cuisines c JOIN restaurant_cuisines rc ON c.id = rc.cuisine_id WHERE rc.restaurant_id = ?";
|
||||
$cuisine_stmt = $db->prepare($cuisine_sql);
|
||||
|
||||
foreach ($restaurants as &$restaurant) {
|
||||
$cuisine_stmt->execute([$restaurant['id']]);
|
||||
$restaurant['cuisines'] = $cuisine_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
}
|
||||
|
||||
echo json_encode($restaurants);
|
||||
exit;
|
||||
}
|
||||
// Fetch featured cuisines
|
||||
$stmt = $pdo->query("SELECT * FROM cuisines ORDER BY name ASC LIMIT 6");
|
||||
$cuisines = $stmt->fetchAll();
|
||||
?>
|
||||
<?php include 'header.php'; ?>
|
||||
<?php include 'hero.php'; ?>
|
||||
|
||||
<div class="container page-content">
|
||||
|
||||
<section class="promo-section">
|
||||
<div class="promo-card">
|
||||
<h3>$0 Delivery Fee</h3>
|
||||
<p>On your first order</p>
|
||||
<div class="container mt-5 mb-5">
|
||||
<section id="how-it-works" class="text-center">
|
||||
<h2 class="section-title">How It Works</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="step">
|
||||
<img src="assets/images/step1.svg" alt="Step 1: Choose a restaurant" class="step-icon">
|
||||
<h3>Choose A Restaurant</h3>
|
||||
<p>Browse from our extensive list of local restaurants.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="step">
|
||||
<img src="assets/images/step2.svg" alt="Step 2: Pick your meal" class="step-icon">
|
||||
<h3>Pick Your Meal</h3>
|
||||
<p>Select your favorite dishes and add them to your cart.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="step">
|
||||
<img src="assets/images/step3.svg" alt="Step 3: Fast delivery" class="step-icon">
|
||||
<h3>Fast Delivery</h3>
|
||||
<p>Get your food delivered right to your doorstep, fast!</p>
|
||||
</div>
|
||||
<div class="promo-card">
|
||||
<h3>Earn Rewards</h3>
|
||||
<p>With every meal</p>
|
||||
</div>
|
||||
<div class="promo-card">
|
||||
<h3>Support Local</h3>
|
||||
<p>Majuro Restaurants</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php
|
||||
// Fetch Top-Rated Restaurants
|
||||
require_once 'db/config.php';
|
||||
$db = db();
|
||||
$top_restaurants_stmt = $db->query("
|
||||
SELECT r.id, r.name, r.image_url, GROUP_CONCAT(c.name SEPARATOR ', ') as cuisines, AVG(rt.rating) as average_rating
|
||||
FROM restaurants r
|
||||
LEFT JOIN restaurant_cuisines rc ON r.id = rc.restaurant_id
|
||||
LEFT JOIN cuisines c ON rc.cuisine_id = c.id
|
||||
LEFT JOIN ratings rt ON r.id = rt.restaurant_id
|
||||
GROUP BY r.id
|
||||
ORDER BY average_rating DESC
|
||||
LIMIT 4
|
||||
");
|
||||
$top_restaurants = $top_restaurants_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Fetch Featured Cuisines
|
||||
$featured_cuisines_stmt = $db->query("
|
||||
SELECT c.id, c.name, c.image_url
|
||||
FROM cuisines c
|
||||
JOIN (
|
||||
SELECT cuisine_id, COUNT(*) as restaurant_count
|
||||
FROM restaurant_cuisines
|
||||
GROUP BY cuisine_id
|
||||
ORDER BY restaurant_count DESC
|
||||
LIMIT 4
|
||||
) as popular_cuisines ON c.id = popular_cuisines.cuisine_id
|
||||
");
|
||||
$featured_cuisines = $featured_cuisines_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
|
||||
<section class="top-rated-restaurants">
|
||||
<h2 class="section-title">Top-rated restaurants</h2>
|
||||
<div class="restaurant-grid">
|
||||
<section id="restaurants" class="mt-5">
|
||||
<h2 class="section-title text-center">Top-Rated Restaurants</h2>
|
||||
<div class="row">
|
||||
<?php if (count($top_restaurants) > 0): ?>
|
||||
<?php foreach ($top_restaurants as $restaurant): ?>
|
||||
<a href="menu.php?restaurant_id=<?php echo $restaurant['id']; ?>" class="restaurant-card">
|
||||
<div class="card-image" style="background-image: url('<?php echo htmlspecialchars($restaurant['image_url'] ?: 'assets/images/hero.jpg'); ?>')"></div>
|
||||
<div class="card-content">
|
||||
<h3><?php echo htmlspecialchars($restaurant['name']); ?></h3>
|
||||
<p class="cuisine-tags"><?php echo htmlspecialchars($restaurant['cuisines']); ?></p>
|
||||
<div class="restaurant-info">
|
||||
<div class="rating-display">
|
||||
<?php if ($restaurant['average_rating']): ?>
|
||||
<span class="star">★</span> <?php echo number_format($restaurant['average_rating'], 1); ?>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card restaurant-card">
|
||||
<a href="menu.php?restaurant_id=<?php echo $restaurant['id']; ?>">
|
||||
<img src="<?php echo htmlspecialchars($restaurant['image_url']); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($restaurant['name']); ?>">
|
||||
</a>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><a href="menu.php?restaurant_id=<?php echo $restaurant['id']; ?>"><?php echo htmlspecialchars($restaurant['name']); ?></a></h5>
|
||||
<p class="card-text text-muted"><?php echo htmlspecialchars(substr($restaurant['description'], 0, 80)); ?>...</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="badge bg-success"><?php echo number_format($restaurant['avg_rating'], 1); ?> ★</span>
|
||||
<a href="menu.php?restaurant_id=<?php echo $restaurant['id']; ?>" class="btn btn-sm btn-outline-primary">View Menu</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
New
|
||||
<p class="text-center">No restaurants found.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="featured-cuisines">
|
||||
<h2 class="section-title">Featured Cuisines</h2>
|
||||
<div class="cuisine-grid">
|
||||
<?php foreach ($featured_cuisines as $cuisine): ?>
|
||||
<a href="index.php?cuisine=<?php echo $cuisine['id']; ?>" class="cuisine-card">
|
||||
<div class="card-image" style="background-image: url('<?php echo htmlspecialchars($cuisine['image_url'] ?: 'https://via.placeholder.com/300x200'); ?>')"></div>
|
||||
<div class="cuisine-card-content">
|
||||
<h3><?php echo htmlspecialchars($cuisine['name']); ?></h3>
|
||||
</div>
|
||||
<section id="cuisines" class="mt-5">
|
||||
<h2 class="section-title text-center">Featured Cuisines</h2>
|
||||
<div class="row justify-content-center">
|
||||
<?php if (count($cuisines) > 0): ?>
|
||||
<?php foreach ($cuisines as $cuisine): ?>
|
||||
<div class="col-md-2 col-sm-4 col-6 text-center mb-4">
|
||||
<a href="restaurants.php?cuisine_id=<?php echo $cuisine['id']; ?>" class="cuisine-card">
|
||||
<img src="<?php echo htmlspecialchars($cuisine['image_url']); ?>" alt="<?php echo htmlspecialchars($cuisine['name']); ?>" class="img-fluid">
|
||||
<h5 class="mt-2"><?php echo htmlspecialchars($cuisine['name']); ?></h5>
|
||||
</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p class="text-center">No cuisines found.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php require_once 'footer.php'; ?>
|
||||
|
||||
144
order_status.php
@ -15,18 +15,29 @@ if (!$order_id) {
|
||||
$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) {
|
||||
// User is logged in, verify order belongs to them
|
||||
$stmt = $pdo->prepare("SELECT o.*, r.name as restaurant_name FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND o.user_id = ?");
|
||||
$stmt->execute([$order_id, $user_id]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$sql .= " AND o.user_id = ?";
|
||||
$params[] = $user_id;
|
||||
} elseif ($token) {
|
||||
// Guest access, verify token
|
||||
$stmt = $pdo->prepare("SELECT o.*, r.name as restaurant_name FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND o.guest_token = ?");
|
||||
$stmt->execute([$order_id, $token]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$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>";
|
||||
@ -165,115 +176,94 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
renderTimeline(data.status);
|
||||
} else if(data.error) {
|
||||
console.error('Error fetching status:', data.error);
|
||||
timelineContainer.innerHTML = '<div class="alert alert-warning">Could not retrieve order status. Please try again later.</div>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fetch error:', error);
|
||||
timelineContainer.innerHTML = '<div class="alert alert-danger">An error occurred while trying to update the order status.</div>';
|
||||
});
|
||||
}
|
||||
|
||||
renderTimeline(currentStatus);
|
||||
setInterval(fetchStatus, 10000); // Poll every 10 seconds
|
||||
|
||||
// --- Map Logic ---
|
||||
// --- New Map Logic ---
|
||||
let map = null;
|
||||
let driverMarker = null;
|
||||
let restaurantMarker = null;
|
||||
let homeMarker = null;
|
||||
const restaurantLocation = {
|
||||
lat: <?php echo $order['restaurant_lat'] ?? 'null'; ?>,
|
||||
lng: <?php echo $order['restaurant_lng'] ?? 'null'; ?>
|
||||
};
|
||||
|
||||
// Define custom icons
|
||||
const driverIcon = L.icon({
|
||||
iconUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png', // A different color or style
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
iconUrl: 'assets/images/driver-icon.svg', // Custom driver icon
|
||||
iconSize: [38, 38],
|
||||
iconAnchor: [19, 38],
|
||||
popupAnchor: [0, -38]
|
||||
});
|
||||
|
||||
const restaurantIcon = L.icon({
|
||||
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-red.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
iconUrl: 'assets/images/restaurant-icon.svg', // Custom restaurant icon
|
||||
iconSize: [38, 38],
|
||||
iconAnchor: [19, 38],
|
||||
popupAnchor: [0, -38]
|
||||
});
|
||||
|
||||
const homeIcon = L.icon({
|
||||
iconUrl: 'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-green.png',
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41],
|
||||
popupAnchor: [1, -34],
|
||||
shadowSize: [41, 41]
|
||||
});
|
||||
function initializeMap() {
|
||||
if (map || !restaurantLocation.lat) return;
|
||||
|
||||
map = L.map('map').setView([restaurantLocation.lat, restaurantLocation.lng], 14);
|
||||
|
||||
function initializeMap(restaurantLocation) {
|
||||
if (map) return;
|
||||
map = L.map('map').setView([restaurantLocation.lat, restaurantLocation.lng], 13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
attribution: '© OpenStreetMap contributors'
|
||||
}).addTo(map);
|
||||
|
||||
restaurantMarker = L.marker([restaurantLocation.lat, restaurantLocation.lng], {icon: restaurantIcon}).addTo(map)
|
||||
.bindPopup(`<b>${restaurantLocation.name}</b>`).openPopup();
|
||||
L.marker([restaurantLocation.lat, restaurantLocation.lng], { icon: restaurantIcon })
|
||||
.addTo(map)
|
||||
.bindPopup('<b><?php echo htmlspecialchars($order['restaurant_name']); ?></b>');
|
||||
}
|
||||
|
||||
function updateMap(data) {
|
||||
if (!data.restaurant_location || !data.restaurant_location.lat) return;
|
||||
|
||||
if (!map) {
|
||||
initializeMap(data.restaurant_location);
|
||||
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;
|
||||
}
|
||||
|
||||
if (data.driver_location && data.driver_location.lat) {
|
||||
const { lat, lng } = data.driver_location;
|
||||
if (driverMarker) {
|
||||
driverMarker.setLatLng([lat, lng]);
|
||||
} else {
|
||||
driverMarker = L.marker([lat, lng], {icon: driverIcon}).addTo(map)
|
||||
.bindPopup('<b>Your Driver</b>');
|
||||
}
|
||||
// Center map between driver and restaurant
|
||||
if (restaurantMarker) {
|
||||
const bounds = L.latLngBounds(driverMarker.getLatLng(), restaurantMarker.getLatLng());
|
||||
map.fitBounds(bounds.pad(0.3));
|
||||
}
|
||||
}
|
||||
|
||||
// Geocode delivery address to show home marker
|
||||
// This requires a geocoding service. For now, we will skip this.
|
||||
// Example using a hypothetical geocoding service:
|
||||
// if (!homeMarker && data.delivery_address) {
|
||||
// geocode(data.delivery_address).then(coords => {
|
||||
// homeMarker = L.marker(coords, {icon: homeIcon}).addTo(map).bindPopup('<b>Your Address</b>');
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
function fetchMapData() {
|
||||
let url = `api/get_order_status.php?order_id=${orderId}`;
|
||||
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.error) {
|
||||
console.error('Error fetching map data:', data.error);
|
||||
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 {
|
||||
updateMap(data);
|
||||
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('Fetch error for map:', error));
|
||||
.catch(error => console.error('Error fetching driver location:', error));
|
||||
}
|
||||
|
||||
fetchMapData();
|
||||
setInterval(fetchMapData, 5000); // Poll every 5 seconds for map updates
|
||||
initializeMap();
|
||||
// Start polling for driver location immediately and repeat every 5 seconds
|
||||
fetchDriverLocation();
|
||||
setInterval(fetchDriverLocation, 5000);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||