Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d8abe32bb | ||
|
|
acff14d6dc | ||
|
|
83696c725a | ||
|
|
8795a633f6 | ||
|
|
2be0d2d4c4 | ||
|
|
42cfa7481c | ||
|
|
44a5c2df2d | ||
|
|
64ad8fe24a | ||
|
|
be8857804b | ||
|
|
4c995f3f6b | ||
|
|
adf8c9c972 | ||
|
|
5fc6fe4ad4 | ||
|
|
7a0a2165fc | ||
|
|
e98192b894 | ||
|
|
c3d7232b7a | ||
|
|
727b6fcf29 | ||
|
|
ec17506c33 | ||
|
|
ef71b241ae | ||
|
|
2c8c9bfe04 | ||
|
|
369f552c9f | ||
|
|
a6434721b7 | ||
|
|
eb2cf1a3fb | ||
|
|
d22c607918 | ||
|
|
793d212dbb | ||
|
|
ab1ae8b39b | ||
|
|
bd0cd9042d |
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
node_modules/
|
||||
*/node_modules/
|
||||
*/build/
|
||||
includes/api_keys.php
|
||||
28
about.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
require_once 'header.php';
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->prepare("SELECT * FROM pages WHERE page_key = :page_key");
|
||||
$stmt->bindValue(':page_key', 'about_us');
|
||||
$stmt->execute();
|
||||
$page = $stmt->fetch();
|
||||
|
||||
?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-8 offset-lg-2">
|
||||
<?php if ($page): ?>
|
||||
<?php echo $page['content']; ?>
|
||||
<?php else: ?>
|
||||
<h1 class="text-center mb-4">About Us</h1>
|
||||
<p class="lead">Content not found. Please set up the 'About Us' page in the admin dashboard.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require_once 'footer.php';
|
||||
?>
|
||||
61
admin/add_menu_item.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$restaurant_id = $_GET['restaurant_id'] ?? null;
|
||||
if (!$restaurant_id) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$description = $_POST['description'] ?? '';
|
||||
$price = $_POST['price'] ?? '';
|
||||
$restaurant_id = $_POST['restaurant_id'] ?? null;
|
||||
|
||||
if ($name && $price && $restaurant_id) {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("INSERT INTO menu_items (restaurant_id, name, description, price) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$restaurant_id, $name, $description, $price]);
|
||||
header('Location: menu.php?restaurant_id=' . $restaurant_id);
|
||||
exit;
|
||||
} else {
|
||||
$error = "Name and price are required.";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Add New Menu Item</h2>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="add_menu_item.php" method="POST">
|
||||
<input type="hidden" name="restaurant_id" value="<?php echo $restaurant_id; ?>">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="price" class="form-label">Price</label>
|
||||
<input type="number" step="0.01" class="form-control" id="price" name="price" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Add Item</button>
|
||||
<a href="menu.php?restaurant_id=<?php echo $restaurant_id; ?>" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
91
admin/add_restaurant.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Fetch all cuisines
|
||||
$cuisines_stmt = $pdo->query("SELECT * FROM cuisines ORDER BY name ASC");
|
||||
$cuisines = $cuisines_stmt->fetchAll();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$description = $_POST['description'] ?? '';
|
||||
$image_url = $_POST['image_url'] ?? '';
|
||||
$selected_cuisines = $_POST['cuisines'] ?? [];
|
||||
|
||||
if ($name && !empty($selected_cuisines)) {
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Insert into restaurants table
|
||||
$stmt = $pdo->prepare("INSERT INTO restaurants (name, description, image_url) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$name, $description, $image_url]);
|
||||
$restaurant_id = $pdo->lastInsertId();
|
||||
|
||||
// Insert into restaurant_cuisines table
|
||||
$cuisine_stmt = $pdo->prepare("INSERT INTO restaurant_cuisines (restaurant_id, cuisine_id) VALUES (?, ?)");
|
||||
foreach ($selected_cuisines as $cuisine_id) {
|
||||
$cuisine_stmt->execute([$restaurant_id, $cuisine_id]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
$error = "Error adding restaurant: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = "Name and at least one cuisine are required.";
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Add New Restaurant</h2>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="add_restaurant.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Cuisines</label>
|
||||
<div class="row">
|
||||
<?php foreach ($cuisines as $cuisine): ?>
|
||||
<div class="col-md-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="cuisines[]" value="<?php echo $cuisine['id']; ?>" id="cuisine_<?php echo $cuisine['id']; ?>">
|
||||
<label class="form-check-label" for="cuisine_<?php echo $cuisine['id']; ?>">
|
||||
<?php echo htmlspecialchars($cuisine['name']); ?>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="image_url" class="form-label">Image URL</label>
|
||||
<input type="text" class="form-control" id="image_url" name="image_url">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Add Restaurant</button>
|
||||
<a href="index.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
41
admin/assign_driver.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
|
||||
session_start();
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['order_id']) && isset($_POST['driver_id'])) {
|
||||
$order_id = $_POST['order_id'];
|
||||
$driver_id = $_POST['driver_id'];
|
||||
|
||||
if (!empty($order_id) && !empty($driver_id)) {
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Check if an assignment already exists
|
||||
$check_stmt = $pdo->prepare("SELECT id FROM driver_assignments WHERE order_id = ?");
|
||||
$check_stmt->execute([$order_id]);
|
||||
|
||||
if (!$check_stmt->fetch()) {
|
||||
// Create new assignment
|
||||
$insert_stmt = $pdo->prepare("INSERT INTO driver_assignments (order_id, driver_id) VALUES (?, ?)");
|
||||
$insert_stmt->execute([$order_id, $driver_id]);
|
||||
|
||||
// Update order status
|
||||
$update_stmt = $pdo->prepare("UPDATE orders SET status = 'Confirmed' WHERE id = ?");
|
||||
$update_stmt->execute([$order_id]);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
// Log error or handle it appropriately
|
||||
die("Database error: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: orders.php');
|
||||
exit;
|
||||
?>
|
||||
134
admin/coupons.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Ensure user is admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$feedback = [];
|
||||
|
||||
// Handle POST requests for creating or toggling coupons
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Create a new coupon
|
||||
if (isset($_POST['create_coupon'])) {
|
||||
$code = trim($_POST['code']);
|
||||
$discount = trim($_POST['discount_percentage']);
|
||||
|
||||
if (!empty($code) && is_numeric($discount) && $discount > 0 && $discount <= 100) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("INSERT INTO coupons (code, discount_percentage) VALUES (:code, :discount)");
|
||||
$stmt->execute(['code' => $code, 'discount' => $discount]);
|
||||
$feedback = ['type' => 'success', 'message' => 'Coupon created successfully.'];
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == '23505') { // Unique violation
|
||||
$feedback = ['type' => 'danger', 'message' => 'Error: Coupon code already exists.'];
|
||||
} else {
|
||||
$feedback = ['type' => 'danger', 'message' => 'Error creating coupon.'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$feedback = ['type' => 'danger', 'message' => 'Invalid input. Please provide a valid code and a discount percentage between 0 and 100.'];
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle coupon status
|
||||
if (isset($_POST['toggle_status'])) {
|
||||
$coupon_id = $_POST['coupon_id'];
|
||||
$current_status = $_POST['current_status'];
|
||||
$new_status = $current_status ? 'false' : 'true';
|
||||
|
||||
$stmt = $pdo->prepare("UPDATE coupons SET is_active = :status WHERE id = :id");
|
||||
$stmt->execute(['status' => $new_status, 'id' => $coupon_id]);
|
||||
$feedback = ['type' => 'success', 'message' => 'Coupon status updated.'];
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all coupons
|
||||
$coupons = $pdo->query("SELECT * FROM coupons ORDER BY created_at DESC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Coupon Management</h2>
|
||||
|
||||
<?php if (!empty($feedback)): ?>
|
||||
<div class="alert alert-<?php echo $feedback['type']; ?>" role="alert">
|
||||
<?php echo htmlspecialchars($feedback['message']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Create Coupon Form -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Create New Coupon</div>
|
||||
<div class="card-body">
|
||||
<form action="coupons.php" method="POST">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="code">Coupon Code</label>
|
||||
<input type="text" name="code" id="code" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="form-group">
|
||||
<label for="discount_percentage">Discount (%)</label>
|
||||
<input type="number" name="discount_percentage" id="discount_percentage" class="form-control" step="0.01" min="0.01" max="100" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button type="submit" name="create_coupon" class="btn btn-primary w-100">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Coupons Table -->
|
||||
<div class="card">
|
||||
<div class="card-header">Existing Coupons</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Discount</th>
|
||||
<th>Status</th>
|
||||
<th>Created At</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($coupons as $coupon): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($coupon['code']); ?></td>
|
||||
<td><?php echo number_format($coupon['discount_percentage'], 2); ?>%</td>
|
||||
<td>
|
||||
<?php if ($coupon['is_active']): ?>
|
||||
<span class="badge bg-success">Active</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo $coupon['created_at']; ?></td>
|
||||
<td>
|
||||
<form action="coupons.php" method="POST">
|
||||
<input type="hidden" name="coupon_id" value="<?php echo $coupon['id']; ?>">
|
||||
<input type="hidden" name="current_status" value="<?php echo $coupon['is_active']; ?>">
|
||||
<button type="submit" name="toggle_status" class="btn btn-sm <?php echo $coupon['is_active'] ? 'btn-warning' : 'btn-success'; ?>">
|
||||
<?php echo $coupon['is_active'] ? 'Deactivate' : 'Activate'; ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
79
admin/cuisines.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
require_once 'header.php';
|
||||
|
||||
$db = db();
|
||||
|
||||
// Handle form submissions for adding, editing, and deleting cuisines
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['add_cuisine'])) {
|
||||
$name = trim($_POST['name']);
|
||||
if (!empty($name)) {
|
||||
$stmt = $db->prepare("INSERT INTO cuisines (name) VALUES (:name) ON CONFLICT (name) DO NOTHING");
|
||||
$stmt->bindValue(':name', $name, PDO::PARAM_STR);
|
||||
$stmt->execute();
|
||||
}
|
||||
} elseif (isset($_POST['delete_cuisine'])) {
|
||||
$id = $_POST['id'];
|
||||
$stmt = $db->prepare("DELETE FROM cuisines WHERE id = :id");
|
||||
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all cuisines
|
||||
$cuisines = $db->query("SELECT * FROM cuisines ORDER BY name ASC")->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<h1 class="text-3xl font-bold mb-6">Manage Cuisines</h1>
|
||||
|
||||
<!-- Add Cuisine Form -->
|
||||
<div class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
|
||||
<h2 class="text-2xl mb-4">Add New Cuisine</h2>
|
||||
<form action="cuisines.php" method="POST">
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="name">
|
||||
Cuisine Name
|
||||
</label>
|
||||
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="name" name="name" type="text" placeholder="e.g., Italian, Mexican" required>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="submit" name="add_cuisine">
|
||||
Add Cuisine
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Cuisines Table -->
|
||||
<div class="bg-white shadow-md rounded px-8 pt-6 pb-8">
|
||||
<h2 class="text-2xl mb-4">Existing Cuisines</h2>
|
||||
<table class="min-w-full table-auto">
|
||||
<thead class="bg-gray-200">
|
||||
<tr>
|
||||
<th class="px-4 py-2">ID</th>
|
||||
<th class="px-4 py-2">Name</th>
|
||||
<th class="px-4 py-2">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($cuisines as $cuisine): ?>
|
||||
<tr class="border-b">
|
||||
<td class="px-4 py-2 text-center"><?php echo htmlspecialchars($cuisine['id']); ?></td>
|
||||
<td class="px-4 py-2"><?php echo htmlspecialchars($cuisine['name']); ?></td>
|
||||
<td class="px-4 py-2 text-center">
|
||||
<form action="cuisines.php" method="POST" onsubmit="return confirm('Are you sure you want to delete this cuisine?');">
|
||||
<input type="hidden" name="id" value="<?php echo $cuisine['id']; ?>">
|
||||
<button type="submit" name="delete_cuisine" class="text-red-500 hover:text-red-700">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
26
admin/delete_menu_item.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
$restaurant_id = $_GET['restaurant_id'] ?? null;
|
||||
|
||||
if ($id) {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("DELETE FROM menu_items WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
|
||||
if ($restaurant_id) {
|
||||
header('Location: menu.php?restaurant_id=' . $restaurant_id);
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
}
|
||||
exit;
|
||||
?>
|
||||
21
admin/delete_restaurant.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
if ($id) {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("DELETE FROM restaurants WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
?>
|
||||
63
admin/drivers.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Fetch all drivers with user information
|
||||
$stmt = $pdo->query("
|
||||
SELECT d.id, d.full_name, d.phone_number, d.vehicle_details, d.approval_status, u.email
|
||||
FROM drivers d
|
||||
JOIN users u ON d.user_id = u.id
|
||||
ORDER BY d.created_at DESC
|
||||
");
|
||||
$drivers = $stmt->fetchAll();
|
||||
|
||||
$possible_statuses = ['pending', 'approved', 'rejected'];
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Driver Management</h2>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Driver ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>Vehicle</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($drivers as $driver): ?>
|
||||
<tr>
|
||||
<td><?php echo $driver['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars($driver['full_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($driver['email']); ?></td>
|
||||
<td><?php echo htmlspecialchars($driver['phone_number']); ?></td>
|
||||
<td><?php echo htmlspecialchars($driver['vehicle_details']); ?></td>
|
||||
<td><?php echo htmlspecialchars($driver['approval_status']); ?></td>
|
||||
<td>
|
||||
<?php if ($driver['approval_status'] == 'pending'): ?>
|
||||
<a href="update_driver_status.php?id=<?php echo $driver['id']; ?>&status=approved" class="btn btn-success btn-sm">Approve</a>
|
||||
<a href="update_driver_status.php?id=<?php echo $driver['id']; ?>&status=rejected" class="btn btn-danger btn-sm">Reject</a>
|
||||
<?php else: ?>
|
||||
N/A
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
73
admin/edit_menu_item.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (!$id) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$description = $_POST['description'] ?? '';
|
||||
$price = $_POST['price'] ?? '';
|
||||
$id = $_POST['id'] ?? null;
|
||||
$restaurant_id = $_POST['restaurant_id'] ?? null;
|
||||
|
||||
if ($name && $price && $id && $restaurant_id) {
|
||||
$stmt = $pdo->prepare("UPDATE menu_items SET name = ?, description = ?, price = ? WHERE id = ?");
|
||||
$stmt->execute([$name, $description, $price, $id]);
|
||||
header('Location: menu.php?restaurant_id=' . $restaurant_id);
|
||||
exit;
|
||||
} else {
|
||||
$error = "Name and price are required.";
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM menu_items WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$item = $stmt->fetch();
|
||||
|
||||
if (!$item) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Edit Menu Item</h2>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="edit_menu_item.php?id=<?php echo $item['id']; ?>" method="POST">
|
||||
<input type="hidden" name="id" value="<?php echo $item['id']; ?>">
|
||||
<input type="hidden" name="restaurant_id" value="<?php echo $item['restaurant_id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($item['name']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description"><?php echo htmlspecialchars($item['description']); ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="price" class="form-label">Price</label>
|
||||
<input type="number" step="0.01" class="form-control" id="price" name="price" value="<?php echo htmlspecialchars($item['price']); ?>" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Update Item</button>
|
||||
<a href="menu.php?restaurant_id=<?php echo $item['restaurant_id']; ?>" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
118
admin/edit_restaurant.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (!$id) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$name = $_POST['name'] ?? '';
|
||||
$description = $_POST['description'] ?? '';
|
||||
$image_url = $_POST['image_url'] ?? '';
|
||||
$selected_cuisines = $_POST['cuisines'] ?? [];
|
||||
$id = $_POST['id'] ?? null;
|
||||
|
||||
if ($name && !empty($selected_cuisines) && $id) {
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Update restaurants table
|
||||
$stmt = $pdo->prepare("UPDATE restaurants SET name = ?, description = ?, image_url = ? WHERE id = ?");
|
||||
$stmt->execute([$name, $description, $image_url, $id]);
|
||||
|
||||
// Delete existing cuisine associations
|
||||
$delete_stmt = $pdo->prepare("DELETE FROM restaurant_cuisines WHERE restaurant_id = ?");
|
||||
$delete_stmt->execute([$id]);
|
||||
|
||||
// Insert new cuisine associations
|
||||
$cuisine_stmt = $pdo->prepare("INSERT INTO restaurant_cuisines (restaurant_id, cuisine_id) VALUES (?, ?)");
|
||||
foreach ($selected_cuisines as $cuisine_id) {
|
||||
$cuisine_stmt->execute([$id, $cuisine_id]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
$pdo->rollBack();
|
||||
$error = "Error updating restaurant: " . $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$error = "Name and at least one cuisine are required.";
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch restaurant details
|
||||
$stmt = $pdo->prepare("SELECT * FROM restaurants WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$restaurant = $stmt->fetch();
|
||||
|
||||
if (!$restaurant) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch all cuisines
|
||||
$cuisines_stmt = $pdo->query("SELECT * FROM cuisines ORDER BY name ASC");
|
||||
$cuisines = $cuisines_stmt->fetchAll();
|
||||
|
||||
// Fetch this restaurant's cuisines
|
||||
$restaurant_cuisines_stmt = $pdo->prepare("SELECT cuisine_id FROM restaurant_cuisines WHERE restaurant_id = ?");
|
||||
$restaurant_cuisines_stmt->execute([$id]);
|
||||
$restaurant_cuisine_ids = $restaurant_cuisines_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Edit Restaurant</h2>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="edit_restaurant.php?id=<?php echo $restaurant['id']; ?>" method="POST">
|
||||
<input type="hidden" name="id" value="<?php echo $restaurant['id']; ?>">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($restaurant['name']); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea class="form-control" id="description" name="description"><?php echo htmlspecialchars($restaurant['description']); ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Cuisines</label>
|
||||
<div class="row">
|
||||
<?php foreach ($cuisines as $cuisine): ?>
|
||||
<div class="col-md-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="cuisines[]" value="<?php echo $cuisine['id']; ?>" id="cuisine_<?php echo $cuisine['id']; ?>" <?php echo in_array($cuisine['id'], $restaurant_cuisine_ids) ? 'checked' : ''; ?>>
|
||||
<label class="form-check-label" for="cuisine_<?php echo $cuisine['id']; ?>">
|
||||
<?php echo htmlspecialchars($cuisine['name']); ?>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="image_url" class="form-label">Image URL</label>
|
||||
<input type="text" class="form-control" id="image_url" name="image_url" value="<?php echo htmlspecialchars($restaurant['image_url']); ?>">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Update Restaurant</button>
|
||||
<a href="index.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
62
admin/export_events.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
|
||||
session_start();
|
||||
if (!isset($_SESSION['is_admin']) || !$_SESSION['is_admin']) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Filters
|
||||
$event_type = $_GET['event_type'] ?? '';
|
||||
$severity = $_GET['severity'] ?? '';
|
||||
$date_range = $_GET['date_range'] ?? '';
|
||||
|
||||
$where_clauses = [];
|
||||
$params = [];
|
||||
|
||||
if ($event_type) {
|
||||
$where_clauses[] = 'event_type = ?';
|
||||
$params[] = $event_type;
|
||||
}
|
||||
|
||||
if ($severity) {
|
||||
$where_clauses[] = 'severity = ?';
|
||||
$params[] = $severity;
|
||||
}
|
||||
|
||||
if ($date_range) {
|
||||
list($start_date, $end_date) = explode(' - ', $date_range);
|
||||
$where_clauses[] = 'timestamp BETWEEN ? AND ?';
|
||||
$params[] = date('Y-m-d 00:00:00', strtotime($start_date));
|
||||
$params[] = date('Y-m-d 23:59:59', strtotime($end_date));
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM system_events";
|
||||
if (!empty($where_clauses)) {
|
||||
$sql .= " WHERE " . implode(' AND ', $where_clauses);
|
||||
}
|
||||
$sql .= " ORDER BY timestamp DESC";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$events = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="system_events_' . date('Y-m-d') . '.csv"');
|
||||
|
||||
$output = fopen('php://output', 'w');
|
||||
|
||||
// Add BOM to fix UTF-8 in Excel
|
||||
fputs($output, "\xEF\xBB\xBF");
|
||||
|
||||
fputcsv($output, array_keys($events[0] ?? []));
|
||||
|
||||
foreach ($events as $event) {
|
||||
fputcsv($output, $event);
|
||||
}
|
||||
|
||||
fclose($output);
|
||||
exit;
|
||||
17
admin/footer.php
Normal file
@ -0,0 +1,17 @@
|
||||
</div>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/jquery/latest/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
$('input[name="date_range"]').daterangepicker({
|
||||
opens: 'left'
|
||||
}, function(start, end, label) {
|
||||
console.log("A new date selection was made: " + start.format('YYYY-MM-DD') + ' to ' + end.format('YYYY-MM-DD'));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
66
admin/header.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Dashboard</title>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
|
||||
<link rel="stylesheet" href="/assets/css/main.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="index.php">Admin Dashboard</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="index.php">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="orders.php">Orders</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="coupons.php">Coupons</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="cuisines.php">Cuisines</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="drivers.php">Drivers</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="settings.php">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="notification_emails.php">Notification Emails</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="system_events.php">System Events</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="pages.php">Pages</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="required_documents.php">Required Documents</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="logout.php">Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container mt-4">
|
||||
197
admin/index.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
require_once '../includes/weather_service.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Update weather data if it's older than 15 minutes
|
||||
$stmt_weather_time = $pdo->query("SELECT timestamp FROM weather_status ORDER BY timestamp DESC LIMIT 1");
|
||||
$last_update_time = $stmt_weather_time->fetchColumn();
|
||||
|
||||
if (!$last_update_time || (time() - strtotime($last_update_time)) > 900) { // 900 seconds = 15 minutes
|
||||
update_weather_data();
|
||||
}
|
||||
|
||||
// Analytics Queries
|
||||
// Total Revenue
|
||||
$stmt_revenue = $pdo->prepare("SELECT SUM(total_price) as total_revenue FROM orders WHERE status = ?");
|
||||
$stmt_revenue->execute(['completed']);
|
||||
$total_revenue = $stmt_revenue->fetchColumn();
|
||||
|
||||
// Total Orders
|
||||
$total_orders = $pdo->query("SELECT COUNT(*) FROM orders")->fetchColumn();
|
||||
|
||||
// Total Customers
|
||||
$total_customers = $pdo->query("SELECT COUNT(*) FROM users WHERE role = 'customer'")->fetchColumn();
|
||||
|
||||
// Most Popular Restaurants
|
||||
$stmt_popular = $pdo->query("
|
||||
SELECT r.name, COUNT(o.id) as order_count
|
||||
FROM restaurants r
|
||||
JOIN orders o ON r.id = o.restaurant_id
|
||||
GROUP BY r.id
|
||||
ORDER BY order_count DESC
|
||||
LIMIT 5
|
||||
");
|
||||
$popular_restaurants = $stmt_popular->fetchAll();
|
||||
|
||||
// Fetch all restaurants for the management table
|
||||
$stmt_restaurants = $pdo->query("SELECT id, name, cuisine FROM restaurants ORDER BY name");
|
||||
$restaurants = $stmt_restaurants->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Admin Dashboard</h2>
|
||||
|
||||
<!-- Analytics Cards -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="card-header">Total Revenue</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">$<?php echo number_format($total_revenue, 2); ?></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-info mb-3">
|
||||
<div class="card-header">Total Orders</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><?php echo $total_orders; ?></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-white bg-success mb-3">
|
||||
<div class="card-header">Total Customers</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><?php echo $total_customers; ?></h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Weather Monitor -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Weather Monitor
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php
|
||||
// Fetch latest weather data
|
||||
$stmt_weather = $pdo->query("SELECT * FROM weather_status ORDER BY timestamp DESC LIMIT 1");
|
||||
$weather = $stmt_weather->fetch();
|
||||
|
||||
if ($weather) {
|
||||
$alert_class = 'bg-success'; // Green for safe
|
||||
if ($weather['precipitation'] > 0 || $weather['wind_speed'] > 10) {
|
||||
$alert_class = 'bg-warning'; // Yellow for caution
|
||||
}
|
||||
if ($weather['alert_status']) {
|
||||
$alert_class = 'bg-danger'; // Red for emergency
|
||||
}
|
||||
?>
|
||||
<div class="alert <?php echo $alert_class; ?> text-white">
|
||||
<strong>Status:</strong> <?php echo htmlspecialchars($weather['alert_message']); ?>
|
||||
</div>
|
||||
<p><strong>Temperature:</strong> <?php echo round($weather['temperature'], 1); ?> °C</p>
|
||||
<p><strong>Wind Speed:</strong> <?php echo round($weather['wind_speed'], 1); ?> m/s</p>
|
||||
<p><strong>Humidity:</strong> <?php echo round($weather['humidity'], 1); ?>%</p>
|
||||
<p><strong>Precipitation (last hour):</strong> <?php echo $weather['precipitation']; ?> mm</p>
|
||||
|
||||
<?php
|
||||
// Emergency Shutdown Button
|
||||
$stmt_shutdown = $pdo->prepare("SELECT value FROM settings WHERE name = ?");
|
||||
$stmt_shutdown->execute(['emergency_shutdown']);
|
||||
$shutdown_status = $stmt_shutdown->fetchColumn();
|
||||
$shutdown_active = ($shutdown_status === 'true');
|
||||
?>
|
||||
<form action="toggle_emergency_shutdown.php" method="POST" class="mt-3">
|
||||
<button type="submit" class="btn <?php echo $shutdown_active ? 'btn-success' : 'btn-danger'; ?>">
|
||||
<?php echo $shutdown_active ? 'Activate Ordering' : 'Emergency Shutdown'; ?>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<?php } else { ?>
|
||||
<p>No weather data available.</p>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Most Popular Restaurants
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($popular_restaurants as $restaurant): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?php echo htmlspecialchars($restaurant['name']); ?>
|
||||
<span class="badge bg-primary rounded-pill"><?php echo $restaurant['order_count']; ?> orders</span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h2 class="mt-5">Restaurant Management</h2>
|
||||
<p><a href="add_restaurant.php" class="btn btn-success">Add New Restaurant</a></p>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Cuisine</th>
|
||||
<th>Location Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$stmt_restaurants = $pdo->query("SELECT id, name, cuisine, location_status FROM restaurants ORDER BY name");
|
||||
$restaurants = $stmt_restaurants->fetchAll();
|
||||
foreach ($restaurants as $restaurant): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($restaurant['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($restaurant['cuisine']); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$status = $restaurant['location_status'];
|
||||
$badge_class = 'badge bg-secondary';
|
||||
if ($status == 'approved') {
|
||||
$badge_class = 'badge bg-success';
|
||||
} elseif ($status == 'rejected') {
|
||||
$badge_class = 'badge bg-danger';
|
||||
}
|
||||
?>
|
||||
<span class="<?php echo $badge_class; ?>"><?php echo htmlspecialchars(ucfirst($status)); ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="edit_restaurant.php?id=<?php echo $restaurant['id']; ?>" class="btn btn-primary btn-sm">Edit</a>
|
||||
<?php if ($status == 'pending_approval'): ?>
|
||||
<a href="update_location_status.php?id=<?php echo $restaurant['id']; ?>&status=approved" class="btn btn-success btn-sm">Approve</a>
|
||||
<a href="update_location_status.php?id=<?php echo $restaurant['id']; ?>&status=rejected" class="btn btn-warning btn-sm">Reject</a>
|
||||
<?php endif; ?>
|
||||
<a href="delete_restaurant.php?id=<?php echo $restaurant['id']; ?>" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this restaurant?');">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
97
admin/login.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
$error = $_SESSION['login_error'] ?? '';
|
||||
unset($_SESSION['login_error']);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Admin Login</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css">
|
||||
<style>
|
||||
body, html {
|
||||
height: 100%;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.main-container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.form-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50%;
|
||||
padding: 40px;
|
||||
}
|
||||
.image-section {
|
||||
width: 50%;
|
||||
background: url('https://images.pexels.com/photos/376464/pexels-photo-376464.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') no-repeat center center;
|
||||
background-size: cover;
|
||||
}
|
||||
.form-container {
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
}
|
||||
.form-container h1 {
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.form-container .form-text {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.form-control {
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
.btn-primary {
|
||||
padding: 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.back-button {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="../index.php" class="btn btn-light back-button"><i class="bi bi-arrow-left"></i> Back to Site</a>
|
||||
<div class="main-container">
|
||||
<div class="form-section">
|
||||
<div class="form-container">
|
||||
<h1>Admin Login</h1>
|
||||
<p class="text-muted">Please enter your credentials to access the admin dashboard.</p>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="login_process.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-section"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
36
admin/login_process.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
$_SESSION['login_error'] = 'Email and password are required.';
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user && password_verify($password, $user['password']) && $user['is_admin']) {
|
||||
$_SESSION['admin_logged_in'] = true;
|
||||
$_SESSION['admin_user_id'] = $user['id'];
|
||||
$_SESSION['admin_email'] = $user['email'];
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
$_SESSION['login_error'] = 'Invalid credentials or not an admin.';
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$_SESSION['login_error'] = 'Database error: ' . $e->getMessage();
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
6
admin/logout.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
64
admin/menu.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$restaurant_id = $_GET['restaurant_id'] ?? null;
|
||||
if (!$restaurant_id) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Get restaurant details
|
||||
$stmt = $pdo->prepare("SELECT name FROM restaurants WHERE id = ?");
|
||||
$stmt->execute([$restaurant_id]);
|
||||
$restaurant = $stmt->fetch();
|
||||
if (!$restaurant) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get menu items for the restaurant
|
||||
$stmt = $pdo->prepare("SELECT * FROM menu_items WHERE restaurant_id = ? ORDER BY name");
|
||||
$stmt->execute([$restaurant_id]);
|
||||
$menu_items = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Manage Menu for <?php echo htmlspecialchars($restaurant['name']); ?></h2>
|
||||
<p><a href="add_menu_item.php?restaurant_id=<?php echo $restaurant_id; ?>" class="btn btn-success">Add New Menu Item</a></p>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Price</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($menu_items as $item): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($item['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($item['description']); ?></td>
|
||||
<td>$<?php echo htmlspecialchars(number_format($item['price'], 2)); ?></td>
|
||||
<td>
|
||||
<a href="edit_menu_item.php?id=<?php echo $item['id']; ?>" class="btn btn-primary btn-sm">Edit</a>
|
||||
<a href="delete_menu_item.php?id=<?php echo $item['id']; ?>&restaurant_id=<?php echo $restaurant_id; ?>" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this item?');">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="index.php" class="btn btn-secondary">Back to Restaurants</a>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
118
admin/notification_emails.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
require_once 'header.php';
|
||||
|
||||
$db = db();
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['add_email'])) {
|
||||
$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
|
||||
$form_type = $_POST['form_type'];
|
||||
if (filter_var($email, FILTER_VALIDATE_EMAIL) && in_array($form_type, ['help', 'driver_signup'])) {
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO email_recipients (email, form_type) VALUES (?, ?)");
|
||||
$stmt->execute([$email, $form_type]);
|
||||
} catch (PDOException $e) {
|
||||
// Ignore unique constraint violations
|
||||
if ($e->getCode() !== '23505') {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['delete_email'])) {
|
||||
$id = $_POST['id'];
|
||||
$stmt = $db->prepare("DELETE FROM email_recipients WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
}
|
||||
// Redirect to the same page to prevent form resubmission
|
||||
header("Location: notification_emails.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch existing emails
|
||||
$stmt = $db->query("SELECT * FROM email_recipients ORDER BY form_type, email");
|
||||
$recipients = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$help_recipients = array_filter($recipients, function($r) { return $r['form_type'] === 'help'; });
|
||||
$driver_recipients = array_filter($recipients, function($r) { return $r['form_type'] === 'driver_signup'; });
|
||||
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Notification Email Recipients</h2>
|
||||
<p>Configure the email addresses that receive notifications when users submit the help or driver signup forms.</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Help Form Recipients
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php if (empty($help_recipients)): ?>
|
||||
<li class="list-group-item text-muted">No recipients configured. Emails will be sent to the default address.</li>
|
||||
<?php else: ?>
|
||||
<?php foreach ($help_recipients as $recipient): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?php echo htmlspecialchars($recipient['email']); ?>
|
||||
<form method="POST" action="notification_emails.php" class="m-0">
|
||||
<input type="hidden" name="id" value="<?php echo $recipient['id']; ?>">
|
||||
<button type="submit" name="delete_email" class="btn btn-danger btn-sm">×</button>
|
||||
</form>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Driver Signup Form Recipients
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php if (empty($driver_recipients)): ?>
|
||||
<li class="list-group-item text-muted">No recipients configured. Emails will be sent to the default address.</li>
|
||||
<?php else: ?>
|
||||
<?php foreach ($driver_recipients as $recipient): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?php echo htmlspecialchars($recipient['email']); ?>
|
||||
<form method="POST" action="notification_emails.php" class="m-0">
|
||||
<input type="hidden" name="id" value="<?php echo $recipient['id']; ?>">
|
||||
<button type="submit" name="delete_email" class="btn btn-danger btn-sm">×</button>
|
||||
</form>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
Add New Recipient
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="notification_emails.php" class="row g-3 align-items-end">
|
||||
<div class="col-md-5">
|
||||
<label for="email" class="form-label">Email address</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label for="form_type" class="form-label">Form Type</label>
|
||||
<select class="form-select" id="form_type" name="form_type">
|
||||
<option value="help">Help Form</option>
|
||||
<option value="driver_signup">Driver Signup Form</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" name="add_email" class="btn btn-primary w-100">Add</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
101
admin/order_details.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
require_once 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
if (!isset($_GET['id'])) {
|
||||
echo "<div class='alert alert-danger'>No order ID specified.</div>";
|
||||
require_once 'footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
$order_id = $_GET['id'];
|
||||
$db = db();
|
||||
|
||||
// Handle status update
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['status'])) {
|
||||
$status = $_POST['status'];
|
||||
$update_stmt = $db->prepare("UPDATE orders SET status = :status WHERE id = :order_id");
|
||||
$update_stmt->bindParam(':status', $status);
|
||||
$update_stmt->bindParam(':order_id', $order_id);
|
||||
$update_stmt->execute();
|
||||
}
|
||||
|
||||
// Fetch order details
|
||||
$order_stmt = $db->prepare("SELECT o.*, u.name AS user_name, u.email AS user_email FROM orders o JOIN users u ON o.user_id = u.id WHERE o.id = :order_id");
|
||||
$order_stmt->bindParam(':order_id', $order_id);
|
||||
$order_stmt->execute();
|
||||
$order = $order_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$order) {
|
||||
echo "<div class='alert alert-danger'>Order not found.</div>";
|
||||
require_once 'footer.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch order items
|
||||
$items_stmt = $db->prepare("SELECT oi.*, p.name AS product_name FROM order_items oi JOIN products p ON oi.product_id = p.id WHERE oi.order_id = :order_id");
|
||||
$items_stmt->bindParam(':order_id', $order_id);
|
||||
$items_stmt->execute();
|
||||
$items = $items_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
|
||||
<h2>Order Details #<?php echo htmlspecialchars($order['id']); ?></h2>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Customer & Order Info</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Customer:</strong> <?php echo htmlspecialchars($order['user_name']); ?></p>
|
||||
<p><strong>Email:</strong> <?php echo htmlspecialchars($order['user_email']); ?></p>
|
||||
<p><strong>Address:</strong> <?php echo htmlspecialchars($order['delivery_address']); ?></p>
|
||||
<p><strong>Total Price:</strong> $<?php echo htmlspecialchars(number_format($order['total_price'], 2)); ?></p>
|
||||
<p><strong>Order Date:</strong> <?php echo htmlspecialchars($order['created_at']); ?></p>
|
||||
<p><strong>Status:</strong> <?php echo htmlspecialchars($order['status']); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">Order Items</div>
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($item['product_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($item['quantity']); ?></td>
|
||||
<td>$<?php echo htmlspecialchars(number_format($item['price'], 2)); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">Update Status</div>
|
||||
<div class="card-body">
|
||||
<form action="order_details.php?id=<?php echo $order['id']; ?>" method="POST">
|
||||
<div class="input-group">
|
||||
<select name="status" class="form-select">
|
||||
<option value="Pending" <?php echo $order['status'] === 'Pending' ? 'selected' : ''; ?>>Pending</option>
|
||||
<option value="Confirmed" <?php echo $order['status'] === 'Confirmed' ? 'selected' : ''; ?>>Confirmed</option>
|
||||
<option value="In Progress" <?php echo $order['status'] === 'In Progress' ? 'selected' : ''; ?>>In Progress</option>
|
||||
<option value="Out for Delivery" <?php echo $order['status'] === 'Out for Delivery' ? 'selected' : ''; ?>>Out for Delivery</option>
|
||||
<option value="Completed" <?php echo $order['status'] === 'Completed' ? 'selected' : ''; ?>>Completed</option>
|
||||
<option value="Cancelled" <?php echo $order['status'] === 'Cancelled' ? 'selected' : ''; ?>>Cancelled</option>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="index.php" class="btn btn-secondary mt-3">Back to Orders</a>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
112
admin/orders.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Handle status update
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['order_id']) && isset($_POST['status'])) {
|
||||
$order_id = $_POST['order_id'];
|
||||
$status = $_POST['status'];
|
||||
|
||||
$update_stmt = $pdo->prepare("UPDATE orders SET status = :status WHERE id = :order_id");
|
||||
$update_stmt->execute(['status' => $status, 'order_id' => $order_id]);
|
||||
|
||||
// Redirect to the same page to prevent form resubmission
|
||||
header('Location: orders.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch all orders with user information
|
||||
$stmt = $pdo->query("
|
||||
SELECT o.id, o.total_price, o.status, o.created_at, u.name as user_name
|
||||
FROM orders o
|
||||
JOIN users u ON o.user_id = u.id
|
||||
ORDER BY o.created_at DESC
|
||||
");
|
||||
$orders = $stmt->fetchAll();
|
||||
|
||||
// Fetch all approved drivers
|
||||
$driver_stmt = $pdo->query("SELECT id, full_name FROM drivers WHERE approval_status = 'approved'");
|
||||
$approved_drivers = $driver_stmt->fetchAll();
|
||||
|
||||
$possible_statuses = ['Pending', 'Confirmed', 'Preparing', 'Out for Delivery', 'Delivered', 'Cancelled'];
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Order Management</h2>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order ID</th>
|
||||
<th>Customer</th>
|
||||
<th>Total Price</th>
|
||||
<th>Order Date</th>
|
||||
<th>Status</th>
|
||||
<th>Update Status</th>
|
||||
<th>Assign Driver</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($orders as $order): ?>
|
||||
<tr>
|
||||
<td><?php echo $order['id']; ?></td>
|
||||
<td><?php echo htmlspecialchars($order['user_name']); ?></td>
|
||||
<td>$<?php echo number_format($order['total_price'], 2); ?></td>
|
||||
<td><?php echo $order['created_at']; ?></td>
|
||||
<td><?php echo htmlspecialchars($order['status']); ?></td>
|
||||
<td>
|
||||
<form action="orders.php" method="POST" class="form-inline">
|
||||
<input type="hidden" name="order_id" value="<?php echo $order['id']; ?>">
|
||||
<div class="form-group">
|
||||
<select name="status" class="form-control form-control-sm">
|
||||
<?php foreach ($possible_statuses as $status): ?>
|
||||
<option value="<?php echo $status; ?>" <?php echo ($order['status'] === $status) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars($status); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-sm ml-2">Update</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
// Check if a driver is already assigned
|
||||
$assignment_stmt = $pdo->prepare("SELECT d.full_name FROM driver_assignments da JOIN drivers d ON da.driver_id = d.id WHERE da.order_id = ?");
|
||||
$assignment_stmt->execute([$order['id']]);
|
||||
$assigned_driver = $assignment_stmt->fetch();
|
||||
?>
|
||||
<?php if ($assigned_driver): ?>
|
||||
<?php echo htmlspecialchars($assigned_driver['full_name']); ?>
|
||||
<?php else: ?>
|
||||
<form action="assign_driver.php" method="POST" class="form-inline">
|
||||
<input type="hidden" name="order_id" value="<?php echo $order['id']; ?>">
|
||||
<div class="form-group">
|
||||
<select name="driver_id" class="form-control form-control-sm">
|
||||
<option value="">Select Driver</option>
|
||||
<?php foreach ($approved_drivers as $driver): ?>
|
||||
<option value="<?php echo $driver['id']; ?>">
|
||||
<?php echo htmlspecialchars($driver['full_name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success btn-sm ml-2">Assign</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
55
admin/pages.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once 'header.php';
|
||||
|
||||
$db = db();
|
||||
$message = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['pages']) && is_array($_POST['pages'])) {
|
||||
foreach ($_POST['pages'] as $page_id => $page_data) {
|
||||
$title = $page_data['title'] ?? '';
|
||||
$content = $page_data['content'] ?? '';
|
||||
|
||||
$stmt = $db->prepare("UPDATE pages SET page_title = :title, content = :content WHERE id = :id");
|
||||
$stmt->bindValue(':title', $title);
|
||||
$stmt->bindValue(':content', $content);
|
||||
$stmt->bindValue(':id', $page_id, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
}
|
||||
$message = '<div class="alert alert-success">Pages updated successfully!</div>';
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $db->query("SELECT * FROM pages ORDER BY id");
|
||||
$pages = $stmt->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="container-fluid">
|
||||
<h1 class="h3 mb-4 text-gray-800">Manage Pages</h1>
|
||||
<?php echo $message; ?>
|
||||
<form method="POST" action="pages.php">
|
||||
<?php foreach ($pages as $page): ?>
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Edit <?php echo htmlspecialchars($page['page_title']); ?></h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<input type="hidden" name="pages[<?php echo $page['id']; ?>][id]" value="<?php echo $page['id']; ?>">
|
||||
<div class="form-group">
|
||||
<label for="page_title_<?php echo $page['id']; ?>">Page Title</label>
|
||||
<input type="text" class="form-control" id="page_title_<?php echo $page['id']; ?>" name="pages[<?php echo $page['id']; ?>][title]" value="<?php echo htmlspecialchars($page['page_title']); ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="page_content_<?php echo $page['id']; ?>">Content</label>
|
||||
<textarea class="form-control" id="page_content_<?php echo $page['id']; ?>" name="pages[<?php echo $page['id']; ?>][content]" rows="10"><?php echo htmlspecialchars($page['content']); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<button type="submit" class="btn btn-primary">Save Changes</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
93
admin/required_documents.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
require_once 'header.php';
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (isset($_POST['add_document'])) {
|
||||
$document_name = trim($_POST['document_name']);
|
||||
$applies_to = trim($_POST['applies_to']);
|
||||
if (!empty($document_name) && !empty($applies_to)) {
|
||||
$db = db();
|
||||
$stmt = $db->prepare("INSERT INTO required_documents (document_name, applies_to) VALUES (?, ?)");
|
||||
$stmt->execute([$document_name, $applies_to]);
|
||||
}
|
||||
} elseif (isset($_POST['delete_document'])) {
|
||||
$document_id = $_POST['document_id'];
|
||||
$db = db();
|
||||
$stmt = $db->prepare("DELETE FROM required_documents WHERE id = ?");
|
||||
$stmt->execute([$document_id]);
|
||||
}
|
||||
header("Location: required_documents.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
// Fetch all required documents
|
||||
$db = db();
|
||||
$stmt = $db->query("SELECT * FROM required_documents ORDER BY applies_to, document_name");
|
||||
$documents = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Manage Required Documents</h2>
|
||||
<p>Define which documents are required for driver and restaurant sign-ups.</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h4>Current Requirements</h4>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Document Name</th>
|
||||
<th>Applies To</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($documents)): ?>
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">No required documents have been defined yet.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($documents as $doc): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($doc['document_name']); ?></td>
|
||||
<td><?php echo ucfirst(htmlspecialchars($doc['applies_to'])); ?></td>
|
||||
<td>
|
||||
<form method="POST" action="required_documents.php" onsubmit="return confirm('Are you sure you want to delete this document requirement?');">
|
||||
<input type="hidden" name="document_id" value="<?php echo $doc['id']; ?>">
|
||||
<button type="submit" name="delete_document" class="btn btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4>Add New Requirement</h4>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="required_documents.php">
|
||||
<div class="mb-3">
|
||||
<label for="document_name" class="form-label">Document Name</label>
|
||||
<input type="text" class="form-control" id="document_name" name="document_name" placeholder="e.g., Driver's License" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="applies_to" class="form-label">Applies To</label>
|
||||
<select class="form-select" id="applies_to" name="applies_to" required>
|
||||
<option value="" disabled selected>Select one...</option>
|
||||
<option value="driver">Driver</option>
|
||||
<option value="restaurant">Restaurant</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" name="add_document" class="btn btn-primary">Add Requirement</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
119
admin/settings.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once '../db/config.php';
|
||||
|
||||
$settings = [];
|
||||
$stmt = db()->query('SELECT * FROM settings');
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$settings[$row['name']] = $row['value'];
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$stmt = db()->prepare('UPDATE settings SET value = :value WHERE name = :name');
|
||||
foreach ($_POST as $name => $value) {
|
||||
$stmt->execute(['value' => $value, 'name' => $name]);
|
||||
}
|
||||
header('Location: settings.php?success=1');
|
||||
exit;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
<div class="alert alert-success">Settings saved successfully!</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5>Fees</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="service_fee_percentage">Service Fee Percentage (%)</label>
|
||||
<input type="number" class="form-control" id="service_fee_percentage" name="service_fee_percentage" value="<?php echo htmlspecialchars($settings['service_fee_percentage'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="delivery_fee">Delivery Fee ($)</label>
|
||||
<input type="number" class="form-control" id="delivery_fee" name="delivery_fee" value="<?php echo htmlspecialchars($settings['delivery_fee'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5>Driver Pay</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6>Base Pay</h6>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="driver_base_pay_tier1_miles">Base Pay Tier 1 (miles)</label>
|
||||
<input type="number" class="form-control" id="driver_base_pay_tier1_miles" name="driver_base_pay_tier1_miles" value="<?php echo htmlspecialchars($settings['driver_base_pay_tier1_miles'] ?? ''); ?>" step="1">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="driver_base_pay_tier1_amount">Base Pay Tier 1 Amount ($)</label>
|
||||
<input type="number" class="form-control" id="driver_base_pay_tier1_amount" name="driver_base_pay_tier1_amount" value="<?php echo htmlspecialchars($settings['driver_base_pay_tier1_amount'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="driver_base_pay_tier2_miles">Base Pay Tier 2 (miles)</label>
|
||||
<input type="number" class="form-control" id="driver_base_pay_tier2_miles" name="driver_base_pay_tier2_miles" value="<?php echo htmlspecialchars($settings['driver_base_pay_tier2_miles'] ?? ''); ?>" step="1">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="driver_base_pay_tier2_amount">Base Pay Tier 2 Amount ($)</label>
|
||||
<input type="number" class="form-control" id="driver_base_pay_tier2_amount" name="driver_base_pay_tier2_amount" value="<?php echo htmlspecialchars($settings['driver_base_pay_tier2_amount'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="driver_base_pay_tier3_amount">Base Pay Tier 3 Amount (> Tier 2 miles) ($)</label>
|
||||
<input type="number" class="form-control" id="driver_base_pay_tier3_amount" name="driver_base_pay_tier3_amount" value="<?php echo htmlspecialchars($settings['driver_base_pay_tier3_amount'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h6>Mileage Pay</h6>
|
||||
<div class="form-group">
|
||||
<label for="driver_mileage_pay_rate">Mileage Pay Rate ($ per mile)</label>
|
||||
<input type="number" class="form-control" id="driver_mileage_pay_rate" name="driver_mileage_pay_rate" value="<?php echo htmlspecialchars($settings['driver_mileage_pay_rate'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h6>Bonuses</h6>
|
||||
<div class="form-group">
|
||||
<label for="driver_busy_hour_bonus">Busy Hour Bonus ($ per delivery)</label>
|
||||
<input type="number" class="form-control" id="driver_busy_hour_bonus" name="driver_busy_hour_bonus" value="<?php echo htmlspecialchars($settings['driver_busy_hour_bonus'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label for="driver_quest_bonus_amount">Quest Bonus Amount ($)</label>
|
||||
<input type="number" class="form-control" id="driver_quest_bonus_amount" name="driver_quest_bonus_amount" value="<?php echo htmlspecialchars($settings['driver_quest_bonus_amount'] ?? ''); ?>" step="0.01">
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="driver_quest_bonus_deliveries">Quest Bonus Deliveries</label>
|
||||
<input type="number" class="form-control" id="driver_quest_bonus_deliveries" name="driver_quest_bonus_deliveries" value="<?php echo htmlspecialchars($settings['driver_quest_bonus_deliveries'] ?? ''); ?>" step="1">
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label for="driver_quest_bonus_hours">Quest Bonus Hours</label>
|
||||
<input type="number" class="form-control" id="driver_quest_bonus_hours" name="driver_quest_bonus_hours" value="<?php echo htmlspecialchars($settings['driver_quest_bonus_hours'] ?? ''); ?>" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
124
admin/system_events.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
require_once 'header.php';
|
||||
require_once '../db/config.php';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Filters
|
||||
$event_type = $_GET['event_type'] ?? '';
|
||||
$severity = $_GET['severity'] ?? '';
|
||||
$date_range = $_GET['date_range'] ?? '';
|
||||
|
||||
$where_clauses = [];
|
||||
$params = [];
|
||||
|
||||
if ($event_type) {
|
||||
$where_clauses[] = 'event_type = ?';
|
||||
$params[] = $event_type;
|
||||
}
|
||||
|
||||
if ($severity) {
|
||||
$where_clauses[] = 'severity = ?';
|
||||
$params[] = $severity;
|
||||
}
|
||||
|
||||
if ($date_range) {
|
||||
list($start_date, $end_date) = explode(' - ', $date_range);
|
||||
$where_clauses[] = 'timestamp BETWEEN ? AND ?';
|
||||
$params[] = date('Y-m-d 00:00:00', strtotime($start_date));
|
||||
$params[] = date('Y-m-d 23:59:59', strtotime($end_date));
|
||||
}
|
||||
|
||||
$sql = "SELECT * FROM system_events";
|
||||
if (!empty($where_clauses)) {
|
||||
$sql .= " WHERE " . implode(' AND ', $where_clauses);
|
||||
}
|
||||
$sql .= " ORDER BY timestamp DESC";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$events = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2>System Events Log</h2>
|
||||
<p class="text-muted">Monitor weather alerts, emergency actions, and system status changes.</p>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary" onclick="location.reload();">🔄 Refresh</button>
|
||||
<a href="export_events.php?<?= http_build_query($_GET) ?>" class="btn btn-secondary">⬇ Export Log (CSV)</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form method="get">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label for="event_type" class="form-label">Event Type</label>
|
||||
<select id="event_type" name="event_type" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="Weather Alert" <?= $event_type === 'Weather Alert' ? 'selected' : '' ?>>Weather Alert</option>
|
||||
<option value="Emergency Shutdown" <?= $event_type === 'Emergency Shutdown' ? 'selected' : '' ?>>Emergency Shutdown</option>
|
||||
<option value="System Restart" <?= $event_type === 'System Restart' ? 'selected' : '' ?>>System Restart</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="date_range" class="form-label">Date Range</label>
|
||||
<input type="text" id="date_range" name="date_range" class="form-control" value="<?= htmlspecialchars($date_range) ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label for="severity" class="form-label">Severity</label>
|
||||
<select id="severity" name="severity" class="form-select">
|
||||
<option value="">All</option>
|
||||
<option value="Info" <?= $severity === 'Info' ? 'selected' : '' ?>>Info</option>
|
||||
<option value="Warning" <?= $severity === 'Warning' ? 'selected' : '' ?>>Warning</option>
|
||||
<option value="Emergency" <?= $severity === 'Emergency' ? 'selected' : '' ?>>Emergency</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary me-2">Apply Filters</button>
|
||||
<a href="system_events.php" class="btn btn-outline-secondary">Clear</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>Event ID</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Event Type</th>
|
||||
<th>Description</th>
|
||||
<th>Severity</th>
|
||||
<th>Triggered By</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($events)):
|
||||
<tr>
|
||||
<td colspan="7" class="text-center">No events found.</td>
|
||||
</tr>
|
||||
<?php else:
|
||||
foreach ($events as $event):
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($event['id']) ?></td>
|
||||
<td><?= htmlspecialchars($event['timestamp']) ?></td>
|
||||
<td><?= htmlspecialchars($event['event_type']) ?></td>
|
||||
<td><?= htmlspecialchars($event['description']) ?></td>
|
||||
<td><?= htmlspecialchars($event['severity']) ?></td>
|
||||
<td><?= htmlspecialchars($event['triggered_by']) ?></td>
|
||||
<td><?= htmlspecialchars($event['status']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach;
|
||||
endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
35
admin/toggle_emergency_shutdown.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Get current shutdown status
|
||||
$stmt_shutdown = $pdo->prepare("SELECT value FROM settings WHERE name = ?");
|
||||
$stmt_shutdown->execute(['emergency_shutdown']);
|
||||
$shutdown_status = $stmt_shutdown->fetchColumn();
|
||||
|
||||
// Toggle the status
|
||||
$new_status = ($shutdown_status === 'true') ? 'false' : 'true';
|
||||
|
||||
// Update or insert the setting
|
||||
$stmt_update = $pdo->prepare("INSERT INTO settings (name, value) VALUES (?, ?) ON CONFLICT (name) DO UPDATE SET value = ?");
|
||||
$stmt_update->execute(['emergency_shutdown', $new_status, $new_status]);
|
||||
|
||||
// Log the event
|
||||
$event_type = ($new_status === 'true') ? 'Emergency Shutdown' : 'System Restart';
|
||||
$description = ($new_status === 'true') ? 'Emergency shutdown activated' : 'System restarted';
|
||||
$severity = ($new_status === 'true') ? 'Emergency' : 'Info';
|
||||
$triggered_by = $_SESSION['admin_email'] ?? 'Admin';
|
||||
$status = ($new_status === 'true') ? 'Ongoing' : 'Resolved';
|
||||
|
||||
$stmt_log = $pdo->prepare("INSERT INTO system_events (event_type, description, severity, triggered_by, status) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt_log->execute([$event_type, $description, $severity, $triggered_by, $status]);
|
||||
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
26
admin/update_driver_status.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
require_once '../db/config.php';
|
||||
|
||||
session_start();
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['id']) && isset($_GET['status'])) {
|
||||
$driver_id = $_GET['id'];
|
||||
$status = $_GET['status'];
|
||||
|
||||
$possible_statuses = ['approved', 'rejected'];
|
||||
|
||||
if (in_array($status, $possible_statuses)) {
|
||||
$pdo = db();
|
||||
$update_stmt = $pdo->prepare("UPDATE drivers SET approval_status = :status WHERE id = :driver_id");
|
||||
$update_stmt->execute(['status' => $status, 'driver_id' => $driver_id]);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: drivers.php');
|
||||
exit;
|
||||
?>
|
||||
60
admin/update_location_status.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is logged in as an admin
|
||||
if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['id']) && isset($_GET['status'])) {
|
||||
$restaurant_id = $_GET['id'];
|
||||
$status = $_GET['status'];
|
||||
|
||||
// Validate status
|
||||
$allowed_statuses = ['approved', 'rejected', 'pending_approval'];
|
||||
if (!in_array($status, $allowed_statuses)) {
|
||||
// Invalid status, redirect back
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$sql = "UPDATE restaurants SET location_status = ? WHERE id = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$status, $restaurant_id]);
|
||||
|
||||
// Get restaurant owner's email
|
||||
$stmt_owner = $pdo->prepare("SELECT u.email, r.name FROM users u JOIN restaurants r ON u.id = r.user_id WHERE r.id = ?");
|
||||
$stmt_owner->execute([$restaurant_id]);
|
||||
$owner_info = $stmt_owner->fetch();
|
||||
|
||||
if ($owner_info) {
|
||||
require_once '../mail/MailService.php';
|
||||
$owner_email = $owner_info['email'];
|
||||
$restaurant_name = $owner_info['name'];
|
||||
$subject = '';
|
||||
$body = '';
|
||||
|
||||
if ($status == 'approved') {
|
||||
$subject = "Your Restaurant Location has been Approved";
|
||||
$body = "<p>Congratulations! The location for your restaurant, <strong>" . htmlspecialchars($restaurant_name) . "</strong>, has been approved.</p><p>Your restaurant is now fully visible to customers.</p>";
|
||||
} elseif ($status == 'rejected') {
|
||||
$subject = "Action Required: Your Restaurant Location has been Rejected";
|
||||
$body = "<p>There was an issue with the location provided for your restaurant, <strong>" . htmlspecialchars($restaurant_name) . "</strong>.</p><p>Please log in to your account and update the location pin for our team to review again.</p>";
|
||||
}
|
||||
|
||||
if (!empty($subject)) {
|
||||
MailService::sendMail($owner_email, $subject, $body);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
// If id or status is not provided, redirect
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
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']
|
||||
]);
|
||||
29
api/get_new_orders.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
session_start();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// Ensure the user is a logged-in restaurant owner
|
||||
if (!isset($_SESSION['restaurant_id'])) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Access denied.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$restaurant_id = $_SESSION['restaurant_id'];
|
||||
$last_check_timestamp = $_GET['since'] ?? '1970-01-01 00:00:00';
|
||||
|
||||
// Fetch new orders since the last check
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT o.id, o.total_price, o.status, o.created_at, u.name as user_name
|
||||
FROM orders o
|
||||
JOIN users u ON o.user_id = u.id
|
||||
WHERE o.restaurant_id = ? AND o.created_at > ?
|
||||
ORDER BY o.created_at DESC
|
||||
");
|
||||
$stmt->execute([$restaurant_id, $last_check_timestamp]);
|
||||
$new_orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode($new_orders);
|
||||
?>
|
||||
70
api/get_order_status.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
session_start();
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
$order_id = $_GET['order_id'] ?? null;
|
||||
$token = $_GET['token'] ?? null;
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
|
||||
if (!$order_id) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Order ID not specified']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$query =
|
||||
'SELECT ' .
|
||||
'o.status, o.delivery_address, o.driver_lat, o.driver_lng, ' .
|
||||
'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) {
|
||||
$query .= ' AND o.user_id = ?';
|
||||
$params[] = $user_id;
|
||||
} elseif ($token) {
|
||||
$query .= ' AND o.guest_token = ?';
|
||||
$params[] = $token;
|
||||
} else {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Permission denied']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt->execute($params);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($order) {
|
||||
// For privacy, we won't return the user's exact address lat/lng.
|
||||
// The frontend will have to geocode the delivery address.
|
||||
// We will add a Google Maps API key for this in a later step.
|
||||
echo json_encode([
|
||||
'status' => ucwords($order['status']),
|
||||
'delivery_address' => $order['delivery_address'],
|
||||
'driver_location' => [
|
||||
'lat' => $order['driver_lat'],
|
||||
'lng' => $order['driver_lng']
|
||||
],
|
||||
'restaurant_location' => [
|
||||
'name' => $order['restaurant_name'],
|
||||
'lat' => $order['restaurant_lat'],
|
||||
'lng' => $order['restaurant_lng']
|
||||
]
|
||||
]);
|
||||
} else {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Order not found or permission denied']);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
19
api/pexels.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__.'/../includes/pexels.php';
|
||||
$q = isset($_GET['query']) ? $_GET['query'] : 'nature';
|
||||
$orientation = isset($_GET['orientation']) ? $_GET['orientation'] : 'portrait';
|
||||
$url = 'https://api.pexels.com/v1/search?query=' . urlencode($q) . '&orientation=' . urlencode($orientation) . '&per_page=1&page=1';
|
||||
$data = pexels_get($url);
|
||||
if (!$data || empty($data['photos'])) { echo json_encode(['error'=>'Failed to fetch image']); exit; }
|
||||
$photo = $data['photos'][0];
|
||||
$src = $photo['src']['large2x'] ?? ($photo['src']['large'] ?? $photo['src']['original']);
|
||||
$target = __DIR__ . '/../assets/images/pexels/' . $photo['id'] . '.jpg';
|
||||
download_to($src, $target);
|
||||
// Return minimal info and local relative path
|
||||
echo json_encode([
|
||||
'id' => $photo['id'],
|
||||
'local' => 'assets/images/pexels/' . $photo['id'] . '.jpg',
|
||||
'photographer' => $photo['photographer'] ?? null,
|
||||
'photographer_url' => $photo['photographer_url'] ?? null,
|
||||
]);
|
||||
49
api/save_location.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
|
||||
// Check if the user is a driver and is logged in
|
||||
if (!isset($_SESSION['driver_id']) || !isset($_SESSION['role']) || $_SESSION['role'] !== 'driver') {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'Forbidden']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$orderId = $data['order_id'] ?? null;
|
||||
$lat = $data['lat'] ?? null;
|
||||
$lng = $data['lng'] ?? null;
|
||||
|
||||
if (!$orderId || !$lat || !$lng) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing required parameters.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
// First, verify the driver is assigned to this order
|
||||
$stmt = $pdo->prepare("SELECT id FROM driver_assignments WHERE order_id = ? AND driver_id = ?");
|
||||
$stmt->execute([$orderId, $_SESSION['driver_id']]);
|
||||
$assignment = $stmt->fetch();
|
||||
|
||||
if (!$assignment) {
|
||||
http_response_code(403);
|
||||
echo json_encode(['error' => 'You are not assigned to this order.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update the order with the driver's location
|
||||
$stmt = $pdo->prepare("UPDATE orders SET driver_lat = ?, driver_lng = ? WHERE id = ?");
|
||||
$stmt->execute([$lat, $lng, $orderId]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
} else {
|
||||
http_response_code(405);
|
||||
echo json_encode(['error' => 'Method Not Allowed']);
|
||||
}
|
||||
74
api/support.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../db/config.php';
|
||||
require_once '../mail/MailService.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Method Not Allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$full_name = trim($_POST['full_name'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$phone = trim($_POST['phone'] ?? null);
|
||||
$subject = trim($_POST['subject'] ?? '');
|
||||
$message = trim($_POST['message'] ?? '');
|
||||
$latitude = !empty($_POST['latitude']) ? trim($_POST['latitude']) : null;
|
||||
$longitude = !empty($_POST['longitude']) ? trim($_POST['longitude']) : null;
|
||||
|
||||
if (empty($full_name) || empty($email) || empty($subject) || empty($message) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Please fill in all required fields and provide a valid email.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$stmt = $db->prepare(
|
||||
"INSERT INTO support_tickets (full_name, email, phone, subject, message, latitude, longitude) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||
);
|
||||
$stmt->execute([$full_name, $email, $phone, $subject, $message, $latitude, $longitude]);
|
||||
|
||||
// Notify support team
|
||||
$support_email_to = 'support@majuroeats.com';
|
||||
$support_email_subject = "New Support Ticket: " . htmlspecialchars($subject);
|
||||
$support_email_html = "
|
||||
<h1>New Support Inquiry</h1>
|
||||
<p><strong>Name:</strong> " . htmlspecialchars($full_name) . "</p>
|
||||
<p><strong>Email:</strong> " . htmlspecialchars($email) . "</p>
|
||||
" . ($phone ? "<p><strong>Phone:</strong> " . htmlspecialchars($phone) . "</p>" : "") . "
|
||||
<p><strong>Subject:</strong> " . htmlspecialchars($subject) . "</p>
|
||||
<p><strong>Message:</strong></p>
|
||||
<p>" . nl2br(htmlspecialchars($message)) . "</p>
|
||||
" . ($latitude && $longitude ? "<p><strong>Location:</strong> <a href=\"https://www.google.com/maps?q={$latitude},{$longitude}\" target=\"_blank\">View on Map</a></p>" : "") . "
|
||||
";
|
||||
$support_email_text = strip_tags($support_email_html);
|
||||
MailService::sendMail($support_email_to, $support_email_subject, $support_email_html, $support_email_text, ['reply_to' => $email]);
|
||||
|
||||
// Send confirmation to user
|
||||
$user_email_subject = "We've received your message | MajuroEats Support";
|
||||
$user_email_html = "
|
||||
<h1>Thank You For Reaching Out!</h1>
|
||||
<p>Hi " . htmlspecialchars($full_name) . ",</p>
|
||||
<p>We've received your support request and a member of our team will get back to you shortly. Here is a copy of your message:</p>
|
||||
<hr>
|
||||
<p><strong>Subject:</strong> " . htmlspecialchars($subject) . "</p>
|
||||
<p><strong>Message:</strong></p>
|
||||
<p>" . nl2br(htmlspecialchars($message)) . "</p>
|
||||
<hr>
|
||||
<p>With thanks,</p>
|
||||
<p>The MajuroEats Team</p>
|
||||
";
|
||||
$user_email_text = strip_tags($user_email_html);
|
||||
MailService::sendMail($email, $user_email_subject, $user_email_html, $user_email_text);
|
||||
|
||||
echo json_encode(['success' => true, 'message' => 'Thank you! Our support team will contact you soon.']);
|
||||
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
// In a real app, you would log this error, not expose it to the user.
|
||||
echo json_encode(['success' => false, 'message' => 'An unexpected error occurred. Please try again later.', 'error' => $e->getMessage()]);
|
||||
}
|
||||
29
apply_coupon.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
if (isset($_POST['coupon_code']) && !empty($_POST['coupon_code'])) {
|
||||
$coupon_code = trim($_POST['coupon_code']);
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM coupons WHERE code = ? AND is_active = 1");
|
||||
$stmt->execute([$coupon_code]);
|
||||
$coupon = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($coupon) {
|
||||
$_SESSION['coupon_id'] = $coupon['id'];
|
||||
$_SESSION['coupon_code'] = $coupon['code'];
|
||||
$_SESSION['discount_percentage'] = $coupon['discount_percentage'];
|
||||
unset($_SESSION['coupon_error']);
|
||||
} else {
|
||||
$_SESSION['coupon_error'] = "Invalid or expired coupon code.";
|
||||
unset($_SESSION['coupon_id']);
|
||||
unset($_SESSION['coupon_code']);
|
||||
unset($_SESSION['discount_percentage']);
|
||||
}
|
||||
} else {
|
||||
$_SESSION['coupon_error'] = "Please enter a coupon code.";
|
||||
}
|
||||
|
||||
header("Location: cart.php");
|
||||
exit();
|
||||
214
assets/css/main.css
Normal file
@ -0,0 +1,214 @@
|
||||
:root {
|
||||
--primary-color: #007bff;
|
||||
--secondary-color: #6c757d;
|
||||
--success-color: #28a745;
|
||||
--danger-color: #dc3545;
|
||||
--warning-color: #ffc107;
|
||||
--info-color: #17a2b8;
|
||||
--light-color: #f8f9fa;
|
||||
--dark-color: #343a40;
|
||||
--font-family-sans-serif: 'Inter', sans-serif;
|
||||
--font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family-sans-serif);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.main-header .navbar-brand {
|
||||
font-weight: 700;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.main-header .nav-link {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
background: url('https://images.pexels.com/photos/1640777/pexels-photo-1640-1.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2') no-repeat center center;
|
||||
background-size: cover;
|
||||
height: 60vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: white;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hero-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
border: none;
|
||||
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 3rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.restaurant-card {
|
||||
border: 1px solid #eee;
|
||||
border-radius: .5rem;
|
||||
transition: transform .2s, box-shadow .2s;
|
||||
}
|
||||
|
||||
.restaurant-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.cuisine-card {
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: var(--dark-color);
|
||||
display: block;
|
||||
transition: transform .2s;
|
||||
}
|
||||
|
||||
.cuisine-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.cuisine-card img {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Restaurant Page Styles */
|
||||
.restaurant-card-new {
|
||||
border: none;
|
||||
border-radius: .75rem;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.restaurant-card-new:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15) !important;
|
||||
}
|
||||
|
||||
.restaurant-card-new .card-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.restaurant-card-new .img-container {
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.restaurant-card-new .card-img-top {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.restaurant-card-new:hover .card-img-top {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.restaurant-card-new .card-body {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.restaurant-card-new .card-footer {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.restaurant-card-new .btn-sm {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.4rem 0.8rem;
|
||||
}
|
||||
|
||||
/* Menu Page Styles */
|
||||
.restaurant-hero-menu {
|
||||
height: 50vh;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.restaurant-hero-menu::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.restaurant-hero-menu-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.menu-item-card-new {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
border: none;
|
||||
border-radius: .75rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menu-item-card-new:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.menu-item-card-new .img-fluid {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.reviews-section .review-card:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.reviews-section hr:last-of-type {
|
||||
display: none;
|
||||
}
|
||||
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 |
BIN
assets/images/hero.jpg
Normal file
|
After Width: | Height: | Size: 319 KiB |
BIN
assets/images/pexels/34251952.jpg
Normal file
|
After Width: | Height: | Size: 417 KiB |
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 |
23
assets/js/main.js
Normal file
@ -0,0 +1,23 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeSwitch = document.querySelector('#checkbox');
|
||||
const body = document.body;
|
||||
|
||||
const currentTheme = localStorage.getItem('theme');
|
||||
|
||||
if (currentTheme) {
|
||||
body.classList.add(currentTheme);
|
||||
if (currentTheme === 'dark-mode') {
|
||||
themeSwitch.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
themeSwitch.addEventListener('change', () => {
|
||||
if (themeSwitch.checked) {
|
||||
body.classList.add('dark-mode');
|
||||
localStorage.setItem('theme', 'dark-mode');
|
||||
} else {
|
||||
body.classList.remove('dark-mode');
|
||||
localStorage.setItem('theme', 'light-mode');
|
||||
}
|
||||
});
|
||||
});
|
||||
BIN
assets/pasted-20251014-221708-657b7936.png
Normal file
|
After Width: | Height: | Size: 110 KiB |
BIN
assets/pasted-20251014-230144-ecd85886.webp
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/pasted-20251014-230507-170c4564.jpg
Normal file
|
After Width: | Height: | Size: 779 KiB |
BIN
assets/pasted-20251015-030330-76b11093.png
Normal file
|
After Width: | Height: | Size: 318 KiB |
BIN
assets/pasted-20251015-151258-a85c3b51.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/pasted-20251015-151954-a05facda.png
Normal file
|
After Width: | Height: | Size: 973 KiB |
BIN
assets/pasted-20251015-151955-4afb19e2.png
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
assets/pasted-20251015-152035-29d5dbfa.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/pasted-20251015-152039-5ff1195a.png
Normal file
|
After Width: | Height: | Size: 827 KiB |
BIN
assets/pasted-20251015-152137-402eb713.png
Normal file
|
After Width: | Height: | Size: 755 KiB |
BIN
assets/pasted-20251015-152159-f3bbf59e.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/pasted-20251015-152218-471cd34e.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
assets/pasted-20251015-152243-150e3dc0.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
assets/pasted-20251015-152405-8fffc81d.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
assets/pasted-20251016-062926-8620c9e4.jpg
Normal file
|
After Width: | Height: | Size: 779 KiB |
BIN
assets/pasted-20251016-083124-7afe909a.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
assets/pasted-20251016-083643-5429546d.jpg
Normal file
|
After Width: | Height: | Size: 779 KiB |
BIN
assets/pasted-20251016-133832-d50b8707.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
assets/pasted-20251016-135055-b29d8da1.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
BIN
assets/pasted-20251016-191630-88be204a.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
assets/pasted-20251016-192041-2abf91d9.jpg
Normal file
|
After Width: | Height: | Size: 779 KiB |
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 |
BIN
assets/pasted-20251016-204625-3165bcc5.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
assets/pasted-20251017-003154-7b32bc9f.png
Normal file
|
After Width: | Height: | Size: 973 KiB |
BIN
assets/pasted-20251017-003440-ba856bda.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
assets/vm-shot-2025-10-16T08-31-07-847Z.jpg
Normal file
|
After Width: | Height: | Size: 77 KiB |
15
auth_google.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/includes/api_keys.php';
|
||||
|
||||
$client = new Google_Client();
|
||||
$client->setClientId(GOOGLE_CLIENT_ID);
|
||||
$client->setClientSecret(GOOGLE_CLIENT_SECRET);
|
||||
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/auth_google_callback.php');
|
||||
$client->addScope("email");
|
||||
$client->addScope("profile");
|
||||
|
||||
header('Location: ' . $client->createAuthUrl());
|
||||
exit();
|
||||
53
auth_google_callback.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/includes/api_keys.php';
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$client = new Google_Client();
|
||||
$client->setClientId(GOOGLE_CLIENT_ID);
|
||||
$client->setClientSecret(GOOGLE_CLIENT_SECRET);
|
||||
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/auth_google_callback.php');
|
||||
|
||||
if (isset($_GET['code'])) {
|
||||
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
|
||||
$client->setAccessToken($token['access_token']);
|
||||
|
||||
$google_oauth = new Google_Service_Oauth2($client);
|
||||
$google_account_info = $google_oauth->userinfo->get();
|
||||
$email = $google_account_info->email;
|
||||
$name = $google_account_info->name;
|
||||
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
|
||||
$stmt->execute([$email]);
|
||||
$user = $stmt->fetch();
|
||||
|
||||
if ($user) {
|
||||
// User exists, log them in
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['user_name'] = $user['name'];
|
||||
$_SESSION['is_admin'] = $user['is_admin'];
|
||||
$_SESSION['role'] = $user['role'];
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
} else {
|
||||
// User doesn't exist, create a new account
|
||||
$stmt = $pdo->prepare("INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)");
|
||||
// We can't store an empty password, so we generate a random one.
|
||||
$random_password = bin2hex(random_bytes(16));
|
||||
$stmt->execute([$name, $email, password_hash($random_password, PASSWORD_DEFAULT), 'customer']);
|
||||
$user_id = $pdo->lastInsertId();
|
||||
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
$_SESSION['user_name'] = $name;
|
||||
$_SESSION['is_admin'] = 0;
|
||||
$_SESSION['role'] = 'customer';
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
header('Location: login.php?error=Google login failed');
|
||||
exit();
|
||||
}
|
||||
125
cart.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'header.php';
|
||||
|
||||
// Fetch cart items
|
||||
$cart_items = [];
|
||||
$total_quantity = 0;
|
||||
$subtotal = 0;
|
||||
$total_bill = 0;
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$session_id = session_id();
|
||||
|
||||
if ($user_id) {
|
||||
$stmt = db()->prepare("
|
||||
SELECT c.menu_item_id, c.quantity, mi.name, mi.price, mi.image_url, r.name as restaurant_name, r.id as restaurant_id
|
||||
FROM cart c
|
||||
JOIN menu_items mi ON c.menu_item_id = mi.id
|
||||
JOIN restaurants r ON c.restaurant_id = r.id
|
||||
WHERE c.user_id = ?
|
||||
");
|
||||
$stmt->execute([$user_id]);
|
||||
$cart_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$stmt = db()->prepare("
|
||||
SELECT c.menu_item_id, c.quantity, mi.name, mi.price, mi.image_url, r.name as restaurant_name, r.id as restaurant_id
|
||||
FROM cart c
|
||||
JOIN menu_items mi ON c.menu_item_id = mi.id
|
||||
JOIN restaurants r ON c.restaurant_id = r.id
|
||||
WHERE c.session_id = ?
|
||||
");
|
||||
$stmt->execute([$session_id]);
|
||||
$cart_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
if ($cart_items) {
|
||||
foreach ($cart_items as $item) {
|
||||
$total_quantity += $item['quantity'];
|
||||
$subtotal += $item['price'] * $item['quantity'];
|
||||
}
|
||||
$total_bill = $subtotal; // For now, total is same as subtotal. Can add taxes/fees later.
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container my-5">
|
||||
<h1 class="display-5 fw-bold mb-4">Your Shopping Cart</h1>
|
||||
|
||||
<?php if (empty($cart_items)): ?>
|
||||
<div class="text-center py-5">
|
||||
<i class="fas fa-shopping-cart fa-4x text-muted mb-3"></i>
|
||||
<h3 class="text-muted">Your cart is empty.</h3>
|
||||
<p class="text-muted">Looks like you haven't added anything to your cart yet.</p>
|
||||
<a href="restaurants.php" class="btn btn-primary mt-3">Continue Shopping</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<?php foreach ($cart_items as $item): ?>
|
||||
<div class="row align-items-center mb-4">
|
||||
<div class="col-md-2">
|
||||
<img src="<?php echo htmlspecialchars($item['image_url'] ?: 'https://via.placeholder.com/150'); ?>" class="img-fluid rounded" alt="<?php echo htmlspecialchars($item['name']); ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h5 class="mb-0"><?php echo htmlspecialchars($item['name']); ?></h5>
|
||||
<small class="text-muted">From: <a href="menu.php?restaurant_id=<?php echo $item['restaurant_id']; ?>"><?php echo htmlspecialchars($item['restaurant_name']); ?></a></small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<form action="cart_actions.php" method="post" class="d-flex align-items-center">
|
||||
<input type="hidden" name="action" value="update">
|
||||
<input type="hidden" name="menu_item_id" value="<?php echo $item['menu_item_id']; ?>">
|
||||
<button type="submit" name="quantity" value="<?php echo $item['quantity'] - 1; ?>" class="btn btn-outline-secondary btn-sm" <?php echo $item['quantity'] <= 1 ? 'disabled' : ''; ?>>-</button>
|
||||
<input type="text" class="form-control form-control-sm text-center mx-2" value="<?php echo $item['quantity']; ?>" readonly style="width: 50px;">
|
||||
<button type="submit" name="quantity" value="<?php echo $item['quantity'] + 1; ?>" class="btn btn-outline-secondary btn-sm">+</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-2 text-end">
|
||||
<span class="fw-bold">$<?php echo number_format($item['price'] * $item['quantity'], 2); ?></span>
|
||||
</div>
|
||||
<div class="col-md-1 text-end">
|
||||
<form action="cart_actions.php" method="post">
|
||||
<input type="hidden" name="action" value="remove">
|
||||
<input type="hidden" name="menu_item_id" value="<?php echo $item['menu_item_id']; ?>">
|
||||
<button type="submit" class="btn btn-link text-danger p-0"><i class="fas fa-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (next($cart_items)): ?>
|
||||
<hr>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title mb-4">Order Summary</h4>
|
||||
<div class="d-flex justify-content-between mb-2">
|
||||
<span>Total Items:</span>
|
||||
<span><?php echo $total_quantity; ?></span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<span>Subtotal:</span>
|
||||
<span>$<?php echo number_format($subtotal, 2); ?></span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-flex justify-content-between fw-bold fs-5">
|
||||
<span>Total Bill:</span>
|
||||
<span>$<?php echo number_format($total_bill, 2); ?></span>
|
||||
</div>
|
||||
<div class="d-grid gap-2 mt-4">
|
||||
<a href="checkout.php" class="btn btn-primary btn-lg">Proceed to Checkout</a>
|
||||
<a href="restaurants.php" class="btn btn-outline-secondary">Continue Shopping</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
81
cart_actions.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
// Default redirect location
|
||||
$redirect_location = 'cart.php';
|
||||
|
||||
// Determine action, prioritizing POST
|
||||
$action = $_POST['action'] ?? $_GET['action'] ?? null;
|
||||
|
||||
if (!$action) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$session_id = session_id();
|
||||
$pdoconnection = db();
|
||||
|
||||
// --- ADD TO CART ---
|
||||
if ($action == 'add' && isset($_POST['menu_item_id'], $_POST['quantity'])) {
|
||||
$menu_item_id = (int)$_POST['menu_item_id'];
|
||||
$quantity = (int)$_POST['quantity'];
|
||||
|
||||
if ($quantity > 0 && $menu_item_id > 0) {
|
||||
// Check if item is already in cart
|
||||
$sql = "SELECT id, quantity FROM cart WHERE menu_item_id = :menu_item_id AND " . ($user_id ? "user_id = :identifier" : "session_id = :identifier");
|
||||
$stmt = $pdoconnection->prepare($sql);
|
||||
$identifier = $user_id ?: $session_id;
|
||||
$stmt->execute([':menu_item_id' => $menu_item_id, ':identifier' => $identifier]);
|
||||
$existing_item = $stmt->fetch();
|
||||
|
||||
if ($existing_item) {
|
||||
// Update quantity
|
||||
$new_quantity = $existing_item['quantity'] + $quantity;
|
||||
$stmt = $pdoconnection->prepare("UPDATE cart SET quantity = :quantity WHERE id = :id");
|
||||
$stmt->execute([':quantity' => $new_quantity, ':id' => $existing_item['id']]);
|
||||
} else {
|
||||
// Insert new item
|
||||
$stmt = $pdoconnection->prepare(
|
||||
"INSERT INTO cart (user_id, session_id, menu_item_id, quantity) VALUES (:user_id, :session_id, :menu_item_id, :quantity)"
|
||||
);
|
||||
$stmt->execute([
|
||||
':user_id' => $user_id,
|
||||
':session_id' => $user_id ? null : $session_id,
|
||||
':menu_item_id' => $menu_item_id,
|
||||
':quantity' => $quantity
|
||||
]);
|
||||
}
|
||||
}
|
||||
// Redirect back to the menu page after adding an item
|
||||
if (isset($_POST['restaurant_id']) && is_numeric($_POST['restaurant_id'])) {
|
||||
$redirect_location = 'menu.php?id=' . $_POST['restaurant_id'];
|
||||
}
|
||||
|
||||
// --- UPDATE CART ---
|
||||
} elseif ($action == 'update' && isset($_POST['cart_id'], $_POST['quantity'])) {
|
||||
$cart_id = (int)$_POST['cart_id'];
|
||||
$quantity = (int)$_POST['quantity'];
|
||||
|
||||
if ($quantity > 0 && $cart_id > 0) {
|
||||
$sql = "UPDATE cart SET quantity = :quantity WHERE id = :cart_id AND " . ($user_id ? "user_id = :identifier" : "session_id = :identifier");
|
||||
$stmt = $pdoconnection->prepare($sql);
|
||||
$identifier = $user_id ?: $session_id;
|
||||
$stmt->execute([':quantity' => $quantity, ':cart_id' => $cart_id, ':identifier' => $identifier]);
|
||||
}
|
||||
|
||||
// --- REMOVE FROM CART ---
|
||||
} elseif ($action == 'remove' && isset($_POST['cart_id'])) {
|
||||
$cart_id = (int)$_POST['cart_id'];
|
||||
|
||||
if ($cart_id > 0) {
|
||||
$sql = "DELETE FROM cart WHERE id = :cart_id AND " . ($user_id ? "user_id = :identifier" : "session_id = :identifier");
|
||||
$stmt = $pdoconnection->prepare($sql);
|
||||
$identifier = $user_id ?: $session_id;
|
||||
$stmt->execute([':cart_id' => $cart_id, ':identifier' => $identifier]);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: ' . $redirect_location);
|
||||
exit;
|
||||
280
checkout.php
Normal file
@ -0,0 +1,280 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/api_keys.php';
|
||||
|
||||
// Fetch emergency shutdown status
|
||||
$stmt_shutdown = db()->prepare("SELECT value FROM settings WHERE name = ?");
|
||||
$stmt_shutdown->execute(['emergency_shutdown']);
|
||||
$shutdown_active = ($stmt_shutdown->fetchColumn() === 'true');
|
||||
|
||||
$is_guest = !isset($_SESSION['user_id']);
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$session_id = session_id();
|
||||
$pdo = db();
|
||||
|
||||
$user = [];
|
||||
if (!$is_guest) {
|
||||
$userStmt = $pdo->prepare("SELECT name, email, address, phone FROM users WHERE id = ?");
|
||||
$userStmt->execute([$user_id]);
|
||||
$user = $userStmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// Fetch cart items
|
||||
if (!$is_guest) {
|
||||
$stmt = $pdo->prepare("SELECT c.id, mi.name, mi.price, c.quantity, r.name as restaurant_name, r.id as restaurant_id FROM cart c JOIN menu_items mi ON c.menu_item_id = mi.id JOIN restaurants r ON mi.restaurant_id = r.id WHERE c.user_id = :user_id");
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("SELECT c.id, mi.name, mi.price, c.quantity, r.name as restaurant_name, r.id as restaurant_id FROM cart c JOIN menu_items mi ON c.menu_item_id = mi.id JOIN restaurants r ON mi.restaurant_id = r.id WHERE c.session_id = :session_id");
|
||||
$stmt->bindParam(':session_id', $session_id);
|
||||
}
|
||||
$stmt->execute();
|
||||
$cartItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($cartItems)) {
|
||||
header("Location: cart.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$subtotal = 0;
|
||||
foreach ($cartItems as $item) {
|
||||
$subtotal += $item['price'] * $item['quantity'];
|
||||
}
|
||||
|
||||
$settingsStmt = $pdo->query("SELECT name, value FROM settings WHERE name IN ('delivery_fee', 'service_fee_percentage')");
|
||||
$settings = $settingsStmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
$delivery_fee = $settings['delivery_fee'] ?? 0;
|
||||
$service_fee_percentage = $settings['service_fee_percentage'] ?? 0;
|
||||
$service_fee = ($subtotal * $service_fee_percentage) / 100;
|
||||
|
||||
$discount_amount = $_SESSION['discount_amount'] ?? 0;
|
||||
$totalPrice = $subtotal + $delivery_fee + $service_fee - $discount_amount;
|
||||
|
||||
$_SESSION['total_price'] = $totalPrice;
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<script src="https://js.stripe.com/v3/"></script>
|
||||
<script src="https://www.paypal.com/sdk/js?client-id=<?php echo $paypalClientId; ?>¤cy=USD"></script>
|
||||
|
||||
<div class="checkout-container">
|
||||
<?php if ($shutdown_active): ?>
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
<h4 class="alert-heading">Ordering Temporarily Disabled</h4>
|
||||
<p>Due to severe weather conditions, we have temporarily suspended all delivery services. The safety of our drivers and customers is our top priority.</p>
|
||||
<hr>
|
||||
<p class="mb-0">We apologize for any inconvenience and will resume operations as soon as it is safe to do so.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="checkout-main">
|
||||
<div class="checkout-header">
|
||||
<a href="index.php" class="checkout-logo">
|
||||
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" fill="currentColor"/><path d="M12 12.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5z" fill="currentColor"/><path d="M12 14c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/></svg>
|
||||
<span>Food Delivery</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="delivery-step">
|
||||
<h3 class="step-title">1. Delivery Details</h3>
|
||||
<form id="delivery-form">
|
||||
<div class="form-group">
|
||||
<label for="name">Full Name</label>
|
||||
<input type="text" id="name" name="name" class="form-control" value="<?php echo htmlspecialchars($user['name'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<?php if ($is_guest): ?>
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
<input type="email" id="email" name="email" class="form-control" required>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="form-group">
|
||||
<label for="address">Delivery Address</label>
|
||||
<input type="text" id="address" name="address" class="form-control" value="<?php echo htmlspecialchars($user['address'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="phone">Phone Number</label>
|
||||
<input type="tel" id="phone" name="phone" class="form-control" value="<?php echo htmlspecialchars($user['phone'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<button type="button" id="to-payment-btn" class="btn-primary">Continue to Payment</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="payment-step" style="display: none;">
|
||||
<h3 class="step-title">2. Payment Method</h3>
|
||||
<div class="payment-methods">
|
||||
<div class="payment-method-card" data-method="stripe">
|
||||
<input type="radio" id="stripe-radio" name="payment_method" value="stripe" checked>
|
||||
<label for="stripe-radio">
|
||||
<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><path d="M42,12H6A2,2,0,0,0,4,14V34a2,2,0,0,0,2,2H42a2,2,0,0,0,2-2V14A2,2,0,0,0,42,12ZM6,16H42v4H6Zm0,16V24H42v8Z" fill="#000"/><rect x="10" y="28" width="8" height="4" fill="#000"/></svg>
|
||||
<span>Credit or Debit Card</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="payment-method-card" data-method="paypal">
|
||||
<input type="radio" id="paypal-radio" name="payment_method" value="paypal">
|
||||
<label for="paypal-radio">
|
||||
<svg viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"><path d="M24,4A20,20,0,1,0,44,24,20,20,0,0,0,24,4Zm11.2,9.45a.7.7,0,0,1,.6,1l-4.6,17.5a.7.7,0,0,1-1.3.1L24,24.25l-5.9,7.8a.7.7,0,0,1-1.1-.9l4.6-17.5a.7.7,0,0,1,1.3-.1L24,19.75l5.9-7.8A.7.7,0,0,1,35.2,13.45Z" fill="#000"/></svg>
|
||||
<span>PayPal</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<form id="payment-form" action="create_stripe_session.php" method="POST">
|
||||
<input type="hidden" name="name" id="hidden_name">
|
||||
<input type="hidden" name="email" id="hidden_email">
|
||||
<input type="hidden" name="address" id="hidden_address">
|
||||
<input type="hidden" name="phone" id="hidden_phone">
|
||||
<button id="stripe-button" class="btn-primary">Pay with Stripe</button>
|
||||
</form>
|
||||
<div id="paypal-button-container" style="display: none;"></div>
|
||||
<button type="button" id="back-to-delivery-btn" class="btn-secondary">Back to Delivery</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="checkout-summary">
|
||||
<h4>Order Summary</h4>
|
||||
<div class="summary-items">
|
||||
<?php foreach ($cartItems as $item): ?>
|
||||
<div class="summary-item">
|
||||
<span class="item-name"><?php echo htmlspecialchars($item['name']); ?> (x<?php echo $item['quantity']; ?>)</span>
|
||||
<span class="item-price">$<?php echo number_format($item['price'] * $item['quantity'], 2); ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="summary-total">
|
||||
<div class="summary-line">
|
||||
<span>Subtotal</span>
|
||||
<span>$<?php echo number_format($subtotal, 2); ?></span>
|
||||
</div>
|
||||
<div class="summary-line">
|
||||
<span>Delivery Fee</span>
|
||||
<span>$<?php echo number_format($delivery_fee, 2); ?></span>
|
||||
</div>
|
||||
<div class="summary-line">
|
||||
<span>Service Fee</span>
|
||||
<span>$<?php echo number_format($service_fee, 2); ?></span>
|
||||
</div>
|
||||
<?php if ($discount_amount > 0): ?>
|
||||
<div class="summary-line discount">
|
||||
<span>Discount</span>
|
||||
<span>-$<?php echo number_format($discount_amount, 2); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="summary-line total">
|
||||
<span>Total</span>
|
||||
<span>$<?php echo number_format($totalPrice, 2); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const deliveryStep = document.getElementById('delivery-step');
|
||||
const paymentStep = document.getElementById('payment-step');
|
||||
const toPaymentBtn = document.getElementById('to-payment-btn');
|
||||
const backToDeliveryBtn = document.getElementById('back-to-delivery-btn');
|
||||
const deliveryForm = document.getElementById('delivery-form');
|
||||
|
||||
const stripeRadio = document.getElementById('stripe-radio');
|
||||
const paypalRadio = document.getElementById('paypal-radio');
|
||||
const stripeButton = document.getElementById('stripe-button');
|
||||
const paypalButtonContainer = document.getElementById('paypal-button-container');
|
||||
|
||||
const nameInput = document.getElementById('name');
|
||||
const emailInput = document.getElementById('email');
|
||||
const addressInput = document.getElementById('address');
|
||||
const phoneInput = document.getElementById('phone');
|
||||
|
||||
const hiddenName = document.getElementById('hidden_name');
|
||||
const hiddenEmail = document.getElementById('hidden_email');
|
||||
const hiddenAddress = document.getElementById('hidden_address');
|
||||
const hiddenPhone = document.getElementById('hidden_phone');
|
||||
|
||||
toPaymentBtn.addEventListener('click', () => {
|
||||
if (deliveryForm.checkValidity()) {
|
||||
// Copy values to hidden fields for Stripe form
|
||||
hiddenName.value = nameInput.value;
|
||||
if (emailInput) {
|
||||
hiddenEmail.value = emailInput.value;
|
||||
}
|
||||
hiddenAddress.value = addressInput.value;
|
||||
hiddenPhone.value = phoneInput.value;
|
||||
|
||||
deliveryStep.style.display = 'none';
|
||||
paymentStep.style.display = 'block';
|
||||
} else {
|
||||
deliveryForm.reportValidity();
|
||||
}
|
||||
});
|
||||
|
||||
backToDeliveryBtn.addEventListener('click', () => {
|
||||
paymentStep.style.display = 'none';
|
||||
deliveryStep.style.display = 'block';
|
||||
});
|
||||
|
||||
function togglePaymentButtons() {
|
||||
if (stripeRadio.checked) {
|
||||
stripeButton.style.display = 'block';
|
||||
paypalButtonContainer.style.display = 'none';
|
||||
} else {
|
||||
stripeButton.style.display = 'none';
|
||||
paypalButtonContainer.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
stripeRadio.addEventListener('change', togglePaymentButtons);
|
||||
paypalRadio.addEventListener('change', togglePaymentButtons);
|
||||
|
||||
document.querySelectorAll('.payment-method-card').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
card.querySelector('input[type="radio"]').checked = true;
|
||||
togglePaymentButtons();
|
||||
});
|
||||
});
|
||||
|
||||
togglePaymentButtons();
|
||||
|
||||
paypal.Buttons({
|
||||
createOrder: function(data, actions) {
|
||||
return actions.order.create({
|
||||
purchase_units: [{
|
||||
amount: {
|
||||
value: '<?php echo number_format($totalPrice, 2, '.', ''); ?>'
|
||||
}
|
||||
}]
|
||||
});
|
||||
},
|
||||
onApprove: function(data, actions) {
|
||||
const formData = new FormData();
|
||||
formData.append('orderID', data.orderID);
|
||||
formData.append('name', nameInput.value);
|
||||
if (emailInput) {
|
||||
formData.append('email', emailInput.value);
|
||||
}
|
||||
formData.append('address', addressInput.value);
|
||||
formData.append('phone', phoneInput.value);
|
||||
|
||||
fetch('paypal-capture.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}).then(res => res.json())
|
||||
.then(details => {
|
||||
if (details.error) {
|
||||
alert(details.error);
|
||||
window.location.href = 'payment-cancel.php';
|
||||
} else {
|
||||
window.location.href = 'order_confirmation.php?order_id=' + details.order_id;
|
||||
}
|
||||
});
|
||||
},
|
||||
onError: function(err) {
|
||||
console.error('PayPal Error:', err);
|
||||
alert('An error occurred with your PayPal payment.');
|
||||
}
|
||||
}).render('#paypal-button-container');
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
7
composer.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"require": {
|
||||
"stripe/stripe-php": "^18.0",
|
||||
"google/apiclient": "^2.0",
|
||||
"aws/aws-sdk-php": "^2.8"
|
||||
}
|
||||
}
|
||||
1573
composer.lock
generated
Normal file
61
create_stripe_session.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/api_keys.php';
|
||||
|
||||
\Stripe\Stripe::setApiKey($stripeSecretKey);
|
||||
|
||||
$total_price = $_SESSION['total_price'] ?? 0;
|
||||
$coupon_id = $_SESSION['coupon_id'] ?? null;
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$is_guest = !$user_id;
|
||||
|
||||
if ($total_price <= 0) {
|
||||
header("Location: cart.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$metadata = [
|
||||
'coupon_id' => $coupon_id
|
||||
];
|
||||
|
||||
$customer_email = null;
|
||||
|
||||
if ($is_guest) {
|
||||
$token = bin2hex(random_bytes(16));
|
||||
$metadata['token'] = $token;
|
||||
$metadata['guest_name'] = $_POST['name'] ?? '';
|
||||
$metadata['guest_email'] = $_POST['email'] ?? '';
|
||||
$metadata['guest_address'] = $_POST['address'] ?? '';
|
||||
$metadata['guest_phone'] = $_POST['phone'] ?? '';
|
||||
$customer_email = $_POST['email'] ?? null;
|
||||
} else {
|
||||
$metadata['user_id'] = $user_id;
|
||||
}
|
||||
|
||||
$checkout_session_params = [
|
||||
'payment_method_types' => ['card'],
|
||||
'line_items' => [[
|
||||
'price_data' => [
|
||||
'currency' => 'usd',
|
||||
'product_data' => [
|
||||
'name' => 'Total Order Amount',
|
||||
],
|
||||
'unit_amount' => $total_price * 100, // Amount in cents
|
||||
],
|
||||
'quantity' => 1,
|
||||
]],
|
||||
'mode' => 'payment',
|
||||
'success_url' => 'http://localhost:8080/payment-success.php?session_id={CHECKOUT_SESSION_ID}',
|
||||
'cancel_url' => 'http://localhost:8080/payment-cancel.php',
|
||||
'metadata' => $metadata
|
||||
];
|
||||
|
||||
if ($customer_email) {
|
||||
$checkout_session_params['customer_email'] = $customer_email;
|
||||
}
|
||||
|
||||
$checkout_session = \Stripe\Checkout\Session::create($checkout_session_params);
|
||||
|
||||
header("Location: " . $checkout_session->url);
|
||||
@ -1,16 +1,19 @@
|
||||
<?php
|
||||
// Generated by setup_mariadb_project.sh — edit as needed.
|
||||
define('DB_HOST', '127.0.0.1');
|
||||
define('DB_NAME', 'app_30953');
|
||||
define('DB_USER', 'app_30953');
|
||||
define('DB_PASS', 'e45f2778-db1f-450c-99c6-29efb4601472');
|
||||
define('DB_DRIVER', 'pgsql');
|
||||
define('DB_HOST', 'aws-1-ap-southeast-2.pooler.supabase.com');
|
||||
define('DB_PORT', '6543');
|
||||
define('DB_NAME', 'postgres');
|
||||
define('DB_USER', 'postgres.ysctoqmvvyshlkkfxsgq');
|
||||
define('DB_PASS', 'qokc12eVuQSlYbew');
|
||||
|
||||
function db() {
|
||||
static $pdo;
|
||||
if (!$pdo) {
|
||||
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
|
||||
$dsn = DB_DRIVER . ':host=' . DB_HOST . ';port=' . DB_PORT . ';dbname=' . DB_NAME;
|
||||
$pdo = new PDO($dsn, DB_USER, DB_PASS, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
]);
|
||||
}
|
||||
return $pdo;
|
||||
|
||||
18
db/run_migration.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
require_once 'config.php';
|
||||
|
||||
$migration_file = end($argv);
|
||||
|
||||
if (!file_exists($migration_file)) {
|
||||
die("Migration file not found: $migration_file");
|
||||
}
|
||||
|
||||
$sql = file_get_contents($migration_file);
|
||||
$db = db();
|
||||
|
||||
try {
|
||||
$db->exec($sql);
|
||||
echo "Migration successful: $migration_file\n";
|
||||
} catch (PDOException $e) {
|
||||
die("Migration failed: " . $e->getMessage() . "\n");
|
||||
}
|
||||
59
driver/accept_job.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
if (!isset($_SESSION['driver_id'])) {
|
||||
header('Location: /driver/login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$order_id = $_POST['order_id'];
|
||||
$driver_id = $_SESSION['driver_id'];
|
||||
|
||||
if (empty($order_id)) {
|
||||
header('Location: index.php?error=Invalid order.');
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Check if the order is still available
|
||||
$check_stmt = $pdo->prepare('SELECT driver_id FROM orders WHERE id = ? AND status = \'ready for pickup\'');
|
||||
$check_stmt->execute([$order_id]);
|
||||
$order = $check_stmt->fetch();
|
||||
|
||||
if (!$order || $order['driver_id'] !== null) {
|
||||
header('Location: index.php?error=Order is no longer available.');
|
||||
$pdo->rollBack();
|
||||
exit;
|
||||
}
|
||||
|
||||
// Assign driver and update status
|
||||
$update_stmt = $pdo->prepare('UPDATE orders SET driver_id = ?, status = \'out for delivery\' WHERE id = ?');
|
||||
$update_stmt->execute([$driver_id, $order_id]);
|
||||
|
||||
// Create driver assignment record
|
||||
$assign_stmt = $pdo->prepare('INSERT INTO driver_assignments (order_id, driver_id) VALUES (?, ?)');
|
||||
$assign_stmt->execute([$order_id, $driver_id]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
header('Location: index.php?success=Order accepted successfully!');
|
||||
exit;
|
||||
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
// Log the error properly in a real application
|
||||
header('Location: index.php?error=An error occurred. Please try again.');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
2
driver/footer.php
Normal file
@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
27
driver/header.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
session_start();
|
||||
// If driver is not logged in, redirect to login page
|
||||
if (!isset($_SESSION['driver_id'])) {
|
||||
header('Location: /driver/login.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Driver Dashboard - Majuro Eats</title>
|
||||
<link rel="stylesheet" href="../assets/css/main.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<a href="/driver/" class="logo">Majuro Eats - Driver Portal</a>
|
||||
<div class="user-actions">
|
||||
<?php if (isset($_SESSION['driver_id'])): ?>
|
||||
<span>Welcome, <?php echo htmlspecialchars($_SESSION['driver_name']); ?></span>
|
||||
<a href="logout.php">Logout</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</header>
|
||||
198
driver/index.php
Normal file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
include 'header.php';
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
$driver_id = $_SESSION['driver_id'];
|
||||
$pdo = db();
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'SELECT ' .
|
||||
'o.id as order_id, ' .
|
||||
'o.status as order_status, ' .
|
||||
'o.delivery_address, ' .
|
||||
'u.name as customer_name, ' .
|
||||
'r.name as restaurant_name, ' .
|
||||
'r.address as restaurant_address ' .
|
||||
'FROM orders o ' .
|
||||
'JOIN driver_assignments da ON o.id = da.order_id ' .
|
||||
'JOIN users u ON o.user_id = u.id ' .
|
||||
'JOIN restaurants r ON o.restaurant_id = r.id ' .
|
||||
'WHERE da.driver_id = ? ' .
|
||||
'ORDER BY o.created_at DESC'
|
||||
);
|
||||
$stmt->execute([$driver_id]);
|
||||
$assigned_orders = $stmt->fetchAll();
|
||||
|
||||
// Get available orders
|
||||
$available_stmt = $pdo->prepare(
|
||||
'SELECT ' .
|
||||
'o.id as order_id, ' .
|
||||
'o.delivery_address, ' .
|
||||
'r.name as restaurant_name, ' .
|
||||
'r.address as restaurant_address ' .
|
||||
'FROM orders o ' .
|
||||
'JOIN restaurants r ON o.restaurant_id = r.id ' .
|
||||
'WHERE o.status = "ready for pickup" AND o.driver_id IS NULL ' .
|
||||
'ORDER BY o.created_at ASC'
|
||||
);
|
||||
$available_stmt->execute();
|
||||
$available_orders = $available_stmt->fetchAll();
|
||||
|
||||
|
||||
$order_statuses = ['out for delivery', 'picked up', 'delivered'];
|
||||
|
||||
?>
|
||||
|
||||
<main class="container">
|
||||
|
||||
<div class="available-jobs">
|
||||
<h2>Available Jobs</h2>
|
||||
<?php if (empty($available_orders)): ?>
|
||||
<p>No jobs available at the moment.</p>
|
||||
<?php else: ?>
|
||||
<div class="order-list">
|
||||
<?php foreach ($available_orders as $order): ?>
|
||||
<div class="order-card">
|
||||
<h3>Order #<?php echo htmlspecialchars($order['order_id']); ?></h3>
|
||||
<p><strong>Restaurant:</strong> <?php echo htmlspecialchars($order['restaurant_name']); ?></p>
|
||||
<p><strong>Restaurant Address:</strong> <?php echo htmlspecialchars($order['restaurant_address']); ?></p>
|
||||
<p><strong>Delivery Address:</strong> <?php echo htmlspecialchars($order['delivery_address']); ?></p>
|
||||
<form action="accept_job.php" method="POST">
|
||||
<input type="hidden" name="order_id" value="<?php echo $order['order_id']; ?>">
|
||||
<button type="submit" class="btn-submit">Accept Job</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h1>My Assigned Deliveries</h1>
|
||||
|
||||
<?php if (isset($_GET['success'])): ?>
|
||||
<p class="success-message"><?php echo htmlspecialchars($_GET['success']); ?></p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_GET['error'])): ?>
|
||||
<p class="error-message"><?php echo htmlspecialchars($_GET['error']); ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="order-list">
|
||||
<?php if (empty($assigned_orders)): ?>
|
||||
<p>You have no assigned orders at the moment.</p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($assigned_orders as $order): ?>
|
||||
<div class="order-card">
|
||||
<h3>Order #<?php echo htmlspecialchars($order['order_id']); ?></h3>
|
||||
<p><strong>Status:</strong> <?php echo htmlspecialchars(ucwords($order['order_status'])); ?></p>
|
||||
<hr>
|
||||
<p><strong>Customer:</strong> <?php echo htmlspecialchars($order['customer_name']); ?></p>
|
||||
<p><strong>Delivery Address:</strong> <?php echo htmlspecialchars($order['delivery_address']); ?></p>
|
||||
<hr>
|
||||
<p><strong>Restaurant:</strong> <?php echo htmlspecialchars($order['restaurant_name']); ?></p>
|
||||
<p><strong>Restaurant Address:</strong> <?php echo htmlspecialchars($order['restaurant_address']); ?></p>
|
||||
|
||||
<form action="update_order_status.php" method="POST" class="status-update-form">
|
||||
<input type="hidden" name="order_id" value="<?php echo $order['order_id']; ?>">
|
||||
<div class="form-group">
|
||||
<label for="status-<?php echo $order['order_id']; ?>">Update Status:</label>
|
||||
<select name="status" id="status-<?php echo $order['order_id']; ?>">
|
||||
<?php foreach ($order_statuses as $status): ?>
|
||||
<option value="<?php echo $status; ?>" <?php echo ($order['order_status'] === $status) ? 'selected' : ''; ?>>
|
||||
<?php echo ucwords($status); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Update</button>
|
||||
</form>
|
||||
|
||||
<div class="delivery-controls">
|
||||
<button class="btn-start-delivery" data-order-id="<?php echo $order['order_id']; ?>">Start Delivery</button>
|
||||
<button class="btn-stop-delivery" data-order-id="<?php echo $order['order_id']; ?>" style="display: none;">Stop Delivery</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const locationWatchers = {};
|
||||
|
||||
document.querySelectorAll('.btn-start-delivery').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const orderId = button.dataset.orderId;
|
||||
|
||||
if (!navigator.geolocation) {
|
||||
alert('Geolocation is not supported by your browser.');
|
||||
return;
|
||||
}
|
||||
|
||||
button.disabled = true;
|
||||
document.querySelector(`.btn-stop-delivery[data-order-id="${orderId}"]`).style.display = 'inline-block';
|
||||
|
||||
|
||||
const watcherId = navigator.geolocation.watchPosition(
|
||||
(position) => {
|
||||
const { latitude, longitude } = position.coords;
|
||||
sendLocation(orderId, latitude, longitude);
|
||||
},
|
||||
(error) => {
|
||||
console.error('Error getting location:', error);
|
||||
alert('Error getting your location. Please ensure you have enabled location services.');
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 0
|
||||
}
|
||||
);
|
||||
|
||||
locationWatchers[orderId] = watcherId;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.btn-stop-delivery').forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const orderId = button.dataset.orderId;
|
||||
if (locationWatchers[orderId]) {
|
||||
navigator.geolocation.clearWatch(locationWatchers[orderId]);
|
||||
delete locationWatchers[orderId];
|
||||
button.style.display = 'none';
|
||||
document.querySelector(`.btn-start-delivery[data-order-id="${orderId}"]`).disabled = false;
|
||||
alert('Delivery stopped.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function sendLocation(orderId, lat, lng) {
|
||||
fetch('/api/save_location.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
order_id: orderId,
|
||||
lat: lat,
|
||||
lng: lng
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
console.log(`Location updated for order ${orderId}`);
|
||||
} else {
|
||||
console.error('Failed to update location:', data.error);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error sending location:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
30
driver/login.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
session_start();
|
||||
if (isset($_SESSION['driver_id'])) {
|
||||
header('Location: /driver/index.php');
|
||||
exit;
|
||||
}
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<main>
|
||||
<div class="auth-container">
|
||||
<h1>Driver Login</h1>
|
||||
<?php if (isset($_GET['error'])): ?>
|
||||
<p class="error-message"><?php echo htmlspecialchars($_GET['error']); ?></p>
|
||||
<?php endif; ?>
|
||||
<form action="login_process.php" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit" class="btn-submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
50
driver/login_process.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$email = $_POST['email'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
if (empty($email) || empty($password)) {
|
||||
header("Location: login.php?error=Please fill all fields");
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
$sql = "SELECT id, full_name, email, password_hash, approval_status FROM drivers WHERE email = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$email]);
|
||||
$driver = $stmt->fetch();
|
||||
|
||||
if ($driver) {
|
||||
if ($driver['approval_status'] === 'pending') {
|
||||
header("Location: ../driver_pending_approval.php");
|
||||
exit;
|
||||
} elseif ($driver['approval_status'] === 'rejected') {
|
||||
header("Location: ../driver_rejected.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($driver['approval_status'] === 'approved' && password_verify($password, $driver['password_hash'])) {
|
||||
$_SESSION['driver_id'] = $driver['id'];
|
||||
$_SESSION['driver_name'] = $driver['full_name'];
|
||||
$_SESSION['role'] = 'driver';
|
||||
header("Location: index.php");
|
||||
exit;
|
||||
} else {
|
||||
header("Location: login.php?error=Invalid credentials");
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
header("Location: login.php?error=Invalid credentials");
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
header("Location: login.php?error=A database error occurred");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
?>
|
||||
7
driver/logout.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
session_start();
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
?>
|
||||
83
driver/update_order_status.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// Ensure driver is logged in
|
||||
if (!isset($_SESSION['driver_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$order_id = $_POST['order_id'];
|
||||
$status = $_POST['status'];
|
||||
$driver_id = $_SESSION['driver_id'];
|
||||
|
||||
$allowed_statuses = ['out for delivery', 'picked up', 'delivered'];
|
||||
|
||||
if (empty($order_id) || empty($status) || !in_array($status, $allowed_statuses)) {
|
||||
header('Location: index.php?error=Invalid input.');
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Security Check: Verify the order is assigned to this driver
|
||||
$check_stmt = $pdo->prepare(
|
||||
'SELECT o.id FROM orders o ' .
|
||||
'JOIN driver_assignments da ON o.id = da.order_id ' .
|
||||
'WHERE o.id = ? AND da.driver_id = ?'
|
||||
);
|
||||
$check_stmt->execute([$order_id, $driver_id]);
|
||||
$assignment = $check_stmt->fetch();
|
||||
|
||||
if (!$assignment) {
|
||||
header('Location: index.php?error=You are not authorized to update this order.');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update the order status
|
||||
$update_stmt = $pdo->prepare('UPDATE orders SET status = ? WHERE id = ?');
|
||||
|
||||
if ($update_stmt->execute([$status, $order_id])) {
|
||||
// Notify customer by email
|
||||
require_once __DIR__ . '/../mail/MailService.php';
|
||||
|
||||
$user_stmt = $pdo->prepare('SELECT u.email, u.name FROM users u JOIN orders o ON u.id = o.user_id WHERE o.id = ?');
|
||||
$user_stmt->execute([$order_id]);
|
||||
$customer = $user_stmt->fetch();
|
||||
|
||||
if ($customer) {
|
||||
$subject = '';
|
||||
$body = '';
|
||||
|
||||
if ($status === 'picked up') {
|
||||
$subject = 'Your order is on its way!';
|
||||
$body = 'Hi ' . $customer['name'] . ',<br><br>Good news! Your order #' . $order_id . ' has been picked up by your driver and is on its way to you.<br><br>Thanks for using Majuro Eats!';
|
||||
} elseif ($status === 'delivered') {
|
||||
$subject = 'Your order has been delivered!';
|
||||
$body = 'Hi ' . $customer['name'] . ',<br><br>Your order #' . $order_id . ' has been delivered. We hope you enjoy your meal!<br><br>Thanks for using Majuro Eats!';
|
||||
}
|
||||
|
||||
if ($subject && $body) {
|
||||
MailService::sendMail($customer['email'], $subject, $body, $body);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: index.php?success=Order status updated successfully.');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php?error=Failed to update order status.');
|
||||
exit;
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
header('Location: index.php?error=A database error occurred.');
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
12
driver_pending_approval.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<main>
|
||||
<div class="container mt-4">
|
||||
<h1>Application Pending Approval</h1>
|
||||
<p>Thank you for signing up to be a driver. Your application is currently under review.</p>
|
||||
<p>We will notify you by email once your application has been approved.</p>
|
||||
<p><a href="logout.php">Logout</a></p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
12
driver_rejected.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<main>
|
||||
<div class="container mt-4">
|
||||
<h1>Application Rejected</h1>
|
||||
<p>We regret to inform you that your application to become a driver has been rejected.</p>
|
||||
<p>If you believe this is a mistake, please contact our support team.</p>
|
||||
<p><a href="logout.php">Logout</a></p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
169
driver_signup.php
Normal file
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->prepare("SELECT id, document_name FROM required_documents WHERE applies_to = 'driver'");
|
||||
$stmt->execute();
|
||||
$required_documents = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<style>
|
||||
.driver-signup-container {
|
||||
display: flex;
|
||||
min-height: 80vh;
|
||||
align-items: stretch;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.driver-signup-promo {
|
||||
flex: 1;
|
||||
background: url('assets/images/hero.jpg') no-repeat center center;
|
||||
background-size: cover;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.driver-signup-promo h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: bold;
|
||||
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.driver-signup-promo p {
|
||||
font-size: 1.25rem;
|
||||
margin-top: 10px;
|
||||
text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
.driver-signup-promo .benefits {
|
||||
margin-top: 30px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.driver-signup-promo .benefits li {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 10px;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.driver-signup-form-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.driver-signup-form {
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.driver-signup-form h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #ff8c00;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 1.1rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-submit:hover {
|
||||
background-color: #e07b00;
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="driver-signup-container">
|
||||
<div class="driver-signup-promo">
|
||||
<h1>Drive with MajuroEats</h1>
|
||||
<p>Earn money on your own schedule.</p>
|
||||
<ul class="benefits">
|
||||
<li>✓ Flexible Hours</li>
|
||||
<li>✓ Weekly Payments</li>
|
||||
<li>✓ Be Your Own Boss</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="driver-signup-form-container">
|
||||
<div class="driver-signup-form">
|
||||
<h2>Create Your Driver Account</h2>
|
||||
<form action="driver_signup_process.php" method="POST" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="full_name">Full Name</label>
|
||||
<input type="text" id="full_name" name="full_name" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="phone_number">Phone Number</label>
|
||||
<input type="text" id="phone_number" name="phone_number" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vehicle_details">Vehicle Details (e.g., 2023 Toyota Camry, Blue)</label>
|
||||
<input type="text" id="vehicle_details" name="vehicle_details" required>
|
||||
</div>
|
||||
|
||||
<?php foreach ($required_documents as $doc): ?>
|
||||
<div class="form-group">
|
||||
<label for="doc_<?php echo htmlspecialchars($doc['id']); ?>"><?php echo htmlspecialchars($doc['document_name']); ?></label>
|
||||
<input type="file" id="doc_<?php echo htmlspecialchars($doc['id']); ?>" name="documents[<?php echo htmlspecialchars($doc['id']); ?>]" class="form-control" required>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<button type="submit" class="btn-submit">Start Earning</button>
|
||||
</form>
|
||||
<div class="form-footer">
|
||||
<p>Already have an account? <a href="login.php">Log in</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
98
driver_signup_process.php
Normal file
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/S3Service.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$full_name = $_POST['full_name'];
|
||||
$email = $_POST['email'];
|
||||
$password = $_POST['password'];
|
||||
$phone_number = $_POST['phone_number'];
|
||||
$vehicle_details = $_POST['vehicle_details'];
|
||||
|
||||
if (empty($full_name) || empty($email) || empty($password) || empty($phone_number) || empty($vehicle_details)) {
|
||||
die('Please fill all required fields.');
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
die('Invalid email format.');
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Check if user already exists
|
||||
$sql = "SELECT id FROM users WHERE email = ?";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$email]);
|
||||
if ($stmt->fetch()) {
|
||||
throw new Exception('Email already exists.');
|
||||
}
|
||||
|
||||
// Insert into users table
|
||||
$password_hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
$sql = "INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, 'driver')";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$full_name, $email, $password_hash]);
|
||||
$user_id = $pdo->lastInsertId();
|
||||
|
||||
// Insert into drivers table
|
||||
$sql = "INSERT INTO drivers (user_id, full_name, phone_number, vehicle_details, approval_status) VALUES (?, ?, ?, ?, 'pending')";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$user_id, $full_name, $phone_number, $vehicle_details]);
|
||||
|
||||
// Handle file uploads
|
||||
if (isset($_FILES['documents'])) {
|
||||
foreach ($_FILES['documents']['tmp_name'] as $doc_id => $tmp_path) {
|
||||
if (!empty($tmp_path) && is_uploaded_file($tmp_path)) {
|
||||
$file_name = $_FILES['documents']['name'][$doc_id];
|
||||
$file_error = $_FILES['documents']['error'][$doc_id];
|
||||
|
||||
if ($file_error !== UPLOAD_ERR_OK) {
|
||||
throw new Exception("Failed to upload file: " . $file_name);
|
||||
}
|
||||
|
||||
$extension = pathinfo($file_name, PATHINFO_EXTENSION);
|
||||
$key = "documents/drivers/{$user_id}/{$doc_id}_" . time() . "." . $extension;
|
||||
|
||||
$s3_url = S3Service::uploadFile($tmp_path, $key);
|
||||
|
||||
if ($s3_url) {
|
||||
$sql = "INSERT INTO user_documents (user_id, document_id, file_path) VALUES (?, ?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$user_id, $doc_id, $s3_url]);
|
||||
} else {
|
||||
throw new Exception("Failed to upload document to S3.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send email notification to admins
|
||||
require_once __DIR__ . '/mail/MailService.php';
|
||||
$stmt_emails = $pdo->prepare("SELECT email FROM email_recipients WHERE form_type = ?");
|
||||
$stmt_emails->execute(['driver_signup']);
|
||||
$recipients = $stmt_emails->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (!empty($recipients)) {
|
||||
$subject = 'New Driver Signup: ' . $full_name;
|
||||
$html_content = "<p>A new driver has signed up and is awaiting approval.</p><p><strong>Name:</strong> {$full_name}</p><p><strong>Email:</strong> {$email}</p><p>Please visit the admin panel to review and approve the application.</p>";
|
||||
$text_content = "A new driver has signed up and is awaiting approval.\nName: {$full_name}\nEmail: {$email}\nPlease visit the admin panel to review and approve the application.";
|
||||
MailService::sendMail($recipients, $subject, $html_content, $text_content);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
header("Location: driver_pending_approval.php");
|
||||
exit;
|
||||
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
die("Error: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
?>
|
||||
73
footer.php
Normal file
@ -0,0 +1,73 @@
|
||||
</main>
|
||||
<footer class="bg-light pt-5 pb-4">
|
||||
<div class="container text-center text-md-start">
|
||||
<div class="row">
|
||||
<div class="col-md-3 col-lg-4 col-xl-3 mx-auto mb-4">
|
||||
<h6 class="text-uppercase fw-bold">MajuroEats</h6>
|
||||
<hr class="mb-4 mt-0 d-inline-block mx-auto" style="width: 60px; background-color: #7c4dff; height: 2px"/>
|
||||
<p>
|
||||
Fast, fresh, and reliable food delivery for the Marshall Islands. Your favorite local restaurants, delivered right to your door.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-2 col-lg-2 col-xl-2 mx-auto mb-4">
|
||||
<h6 class="text-uppercase fw-bold">Links</h6>
|
||||
<hr class="mb-4 mt-0 d-inline-block mx-auto" style="width: 60px; background-color: #7c4dff; height: 2px"/>
|
||||
<p><a href="about.php" class="text-dark">About Us</a></p>
|
||||
<p><a href="restaurants.php" class="text-dark">Restaurants</a></p>
|
||||
<p><a href="driver_signup.php" class="text-dark">Become a Driver</a></p>
|
||||
<p><a href="help.php" class="text-dark">Help</a></p>
|
||||
</div>
|
||||
<div class="col-md-3 col-lg-2 col-xl-2 mx-auto mb-4">
|
||||
<h6 class="text-uppercase fw-bold">Legal</h6>
|
||||
<hr class="mb-4 mt-0 d-inline-block mx-auto" style="width: 60px; background-color: #7c4dff; height: 2px"/>
|
||||
<p><a href="#!" class="text-dark">Terms of Service</a></p>
|
||||
<p><a href="#!" class="text-dark">Privacy Policy</a></p>
|
||||
</div>
|
||||
<div class="col-md-4 col-lg-3 col-xl-3 mx-auto mb-md-0 mb-4">
|
||||
<h6 class="text-uppercase fw-bold">Contact</h6>
|
||||
<hr class="mb-4 mt-0 d-inline-block mx-auto" style="width: 60px; background-color: #7c4dff; height: 2px"/>
|
||||
<p><i class="fas fa-home me-3"></i> Majuro, MH 96960</p>
|
||||
<p><i class="fas fa-envelope me-3"></i> contact@majuroeats.com</p>
|
||||
<p><i class="fas fa-phone me-3"></i> + 692 123 4567</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center p-3" style="background-color: rgba(0, 0, 0, 0.05);">
|
||||
© <?php echo date("Y"); ?> Copyright:
|
||||
<a class="text-dark" href="/">MajuroEats.com</a>
|
||||
| <a class="text-dark" href="/admin/login.php">Admin Login</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
|
||||
<!--Start of Tawk.to Script-->
|
||||
<script type="text/javascript">
|
||||
var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();
|
||||
(function(){
|
||||
var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];
|
||||
s1.async=true;
|
||||
s1.src='https://embed.tawk.to/68ed54c73744141950704532/1j7fh6b6i';
|
||||
s1.charset='UTF-8';
|
||||
s1.setAttribute('crossorigin','*');
|
||||
s0.parentNode.insertBefore(s1,s0);
|
||||
})();
|
||||
</script>
|
||||
<!--End of Tawk.to Script-->
|
||||
|
||||
<!-- OneSignal Push Notifications -->
|
||||
<script src="https://cdn.onesignal.com/sdks/OneSignalSDK.js" async=""></script>
|
||||
<script>
|
||||
window.OneSignal = window.OneSignal || [];
|
||||
OneSignal.push(function() {
|
||||
OneSignal.init({
|
||||
appId: "<?php echo getenv('ONESIGNAL_APP_ID'); ?>",
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php if (extension_loaded('newrelic')) { echo newrelic_get_browser_timing_footer(); } ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
74
forgot_password.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'mail/MailService.php';
|
||||
|
||||
$page_title = "Forgot Password";
|
||||
$message = '';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['email'])) {
|
||||
$email = $_POST['email'];
|
||||
$db = db();
|
||||
|
||||
// Check if user with that email exists
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE email = :email");
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->execute();
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($user) {
|
||||
// Generate a unique token
|
||||
$token = bin2hex(random_bytes(50));
|
||||
|
||||
// Store the token in the password_resets table
|
||||
$stmt = $db->prepare("DELETE FROM password_resets WHERE email = :email");
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->execute();
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO password_resets (email, token) VALUES (:email, :token)");
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->bindParam(':token', $token);
|
||||
$stmt->execute();
|
||||
|
||||
// Send the password reset email
|
||||
$reset_link = "http://" . $_SERVER['HTTP_HOST'] . "/reset_password.php?token=" . $token;
|
||||
$subject = "Password Reset Request";
|
||||
$body = "Click on this link to reset your password: <a href=''' . $reset_link . '''>''' . $reset_link . '''</a>";
|
||||
|
||||
MailService::sendMail($email, $subject, $body, strip_tags($body));
|
||||
|
||||
$message = "If an account with that email exists, a password reset link has been sent.";
|
||||
} else {
|
||||
$message = "If an account with that email exists, a password reset link has been sent.";
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>Forgot Password</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-info"><?php echo $message; ?></div>
|
||||
<?php endif; ?>
|
||||
<p>Please enter your email address. You will receive a link to create a new password via email.</p>
|
||||
<form action="forgot_password.php" method="post">
|
||||
<div class="form-group mb-3">
|
||||
<label for="email">Email address</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Send Password Reset Email</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
76
header.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// Cart item count
|
||||
$cart_item_count = 0;
|
||||
if (!empty($_SESSION['user_id'])) {
|
||||
$stmt = db()->prepare("SELECT SUM(quantity) FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$_SESSION['user_id']]);
|
||||
$cart_item_count = $stmt->fetchColumn();
|
||||
} else {
|
||||
$stmt = db()->prepare("SELECT SUM(quantity) FROM cart WHERE session_id = ?");
|
||||
$stmt->execute([session_id()]);
|
||||
$cart_item_count = $stmt->fetchColumn();
|
||||
}
|
||||
$cart_item_count = $cart_item_count ?: 0;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>MajuroEats - Fast Island Delivery</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
|
||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css">
|
||||
<link rel="stylesheet" href="assets/css/main.css?v=<?php echo uniqid(); ?>">
|
||||
<script src="assets.js" defer></script>
|
||||
<?php if (extension_loaded('newrelic')) { echo newrelic_get_browser_timing_header(); } ?>
|
||||
</head>
|
||||
<body>
|
||||
<header class="main-header bg-light shadow-sm">
|
||||
<div class="container">
|
||||
<nav class="navbar navbar-expand-lg bg-light">
|
||||
<a href="/" class="navbar-brand fw-bold">
|
||||
<span style="font-size: 1.5rem;">🌊</span>
|
||||
MajuroEats
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="mainNav">
|
||||
<ul class="navbar-nav mx-auto">
|
||||
<li class="nav-item"><a class="nav-link" href="index.php">Home</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="restaurants.php">Restaurants</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="about.php">About Us</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="how-it-works.php">How It Works</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="rewards.php">Rewards</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="driver_signup.php">Become a Driver</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="help.php">Help</a></li>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="cart.php" class="btn btn-outline-primary position-relative me-3">
|
||||
<i class="fas fa-shopping-cart"></i>
|
||||
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
||||
<?php echo $cart_item_count; ?>
|
||||
<span class="visually-hidden">items in cart</span>
|
||||
</span>
|
||||
</a>
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<a href="profile.php" class="btn btn-outline-secondary me-2">Profile</a>
|
||||
<a href="logout.php" class="btn btn-primary">Logout</a>
|
||||
<?php else: ?>
|
||||
<a href="login.php" class="btn btn-outline-secondary me-2">Sign In</a>
|
||||
<a href="signup.php" class="btn btn-primary">Sign Up</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container mt-4">
|
||||
243
help.php
Normal file
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'header.php';
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$stmt = $db->query("SELECT question, answer FROM faqs ORDER BY sort_order ASC");
|
||||
$faqs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} catch (PDOException $e) {
|
||||
// Handle DB error gracefully
|
||||
$faqs = [];
|
||||
}
|
||||
|
||||
require_once 'includes/api_keys.php';
|
||||
|
||||
// GOOGLE_MAPS_API_KEY is needed for the map functionality.
|
||||
$google_maps_api_key = defined('GOOGLE_MAPS_API_KEY') ? GOOGLE_MAPS_API_KEY : '';
|
||||
|
||||
?>
|
||||
|
||||
<style>
|
||||
.hero-section {
|
||||
background: linear-gradient(to bottom, #40E0D0, #FFFFFF);
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
.hero-section h1 {
|
||||
font-weight: bold;
|
||||
font-size: 3rem;
|
||||
color: #343a40;
|
||||
}
|
||||
.hero-section p {
|
||||
font-size: 1.25rem;
|
||||
color: #555;
|
||||
}
|
||||
.tropical-header-image {
|
||||
width: 100%;
|
||||
height: 250px;
|
||||
object-fit: cover;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.contact-form-section, .faq-section {
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 6px 20px rgba(0,0,0,0.08);
|
||||
}
|
||||
#map {
|
||||
height: 250px;
|
||||
border-radius: 8px;
|
||||
background-color: #e9ecef;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.accordion-button:not(.collapsed) {
|
||||
color: #007bff;
|
||||
background-color: #e7f1ff;
|
||||
}
|
||||
.accordion-button:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
#confirmation-message {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid hero-section">
|
||||
<div class="container">
|
||||
<h1>We’re here to help</h1>
|
||||
<p>Whether you have a question, concern, or just want to say Iakwe! 🌴</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container my-5">
|
||||
<img src="https://images.pexels.com/photos/1671325/pexels-photo-1671325.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1" alt="Tropical Header" class="tropical-header-image shadow-lg">
|
||||
|
||||
<div id="confirmation-message" class="alert alert-success"></div>
|
||||
|
||||
<div class="row g-5">
|
||||
<div class="col-lg-6">
|
||||
<div class="contact-form-section">
|
||||
<h3 class="mb-4">Send Us a Message</h3>
|
||||
<form id="contact-form">
|
||||
<div class="mb-3">
|
||||
<label for="full_name" class="form-label">Full Name</label>
|
||||
<input type="text" class="form-control" id="full_name" name="full_name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email Address</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">Phone Number (Optional)</label>
|
||||
<input type="tel" class="form-control" id="phone" name="phone">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="subject" class="form-label">Subject</label>
|
||||
<select class="form-select" id="subject" name="subject" required>
|
||||
<option value="" disabled selected>Select a subject</option>
|
||||
<option value="Order Issue">Order Issue</option>
|
||||
<option value="Account Help">Account Help</option>
|
||||
<option value="Restaurant Inquiry">Restaurant Inquiry</option>
|
||||
<option value="Feedback">Feedback</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">How can we help?</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="5" required></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Map Section -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Optional: Pin your delivery location so we can assist you better.</label>
|
||||
<?php if ($google_maps_api_key): ?>
|
||||
<div id="map"></div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning">
|
||||
Map functionality is currently unavailable. Please provide a Google Maps API key.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<input type="hidden" id="latitude" name="latitude">
|
||||
<input type="hidden" id="longitude" name="longitude">
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Send Message</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="faq-section">
|
||||
<h3 class="mb-4">Frequently Asked Questions</h3>
|
||||
<div class="accordion" id="faqAccordion">
|
||||
<?php if (!empty($faqs)): ?>
|
||||
<?php foreach ($faqs as $index => $faq): ?>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading<?php echo $index; ?>">
|
||||
<button class="accordion-button <?php echo $index > 0 ? 'collapsed' : ''; ?>" type="button" data-bs-toggle="collapse" data-bs-target="#collapse<?php echo $index; ?>" aria-expanded="<?php echo $index === 0 ? 'true' : 'false'; ?>" aria-controls="collapse<?php echo $index; ?>">
|
||||
<?php echo htmlspecialchars($faq['question']); ?>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse<?php echo $index; ?>" class="accordion-collapse collapse <?php echo $index === 0 ? 'show' : ''; ?>" aria-labelledby="heading<?php echo $index; ?>" data-bs-parent="#faqAccordion">
|
||||
<div class="accordion-body">
|
||||
<?php echo nl2br(htmlspecialchars($faq['answer'])); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p>No FAQs found.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const contactForm = document.getElementById('contact-form');
|
||||
const confirmationMessage = document.getElementById('confirmation-message');
|
||||
|
||||
contactForm.addEventListener('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(contactForm);
|
||||
const submitButton = contactForm.querySelector('button[type="submit"]');
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = 'Sending...';
|
||||
|
||||
fetch('/api/support.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
confirmationMessage.textContent = data.message;
|
||||
confirmationMessage.className = data.success ? 'alert alert-success' : 'alert alert-danger';
|
||||
confirmationMessage.style.display = 'block';
|
||||
|
||||
if (data.success) {
|
||||
contactForm.reset();
|
||||
if (window.marker) {
|
||||
window.marker.setMap(null); // Remove marker from map
|
||||
}
|
||||
}
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
})
|
||||
.catch(error => {
|
||||
confirmationMessage.textContent = 'An error occurred. Please try again.';
|
||||
confirmationMessage.className = 'alert alert-danger';
|
||||
confirmationMessage.style.display = 'block';
|
||||
})
|
||||
.finally(() => {
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = 'Send Message';
|
||||
});
|
||||
});
|
||||
|
||||
<?php if ($google_maps_api_key): ?>
|
||||
// Load Google Maps script
|
||||
const script = document.createElement('script');
|
||||
script.src = `https://maps.googleapis.com/maps/api/js?key=<?php echo $google_maps_api_key; ?>&callback=initMap`;
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
document.head.appendChild(script);
|
||||
<?php endif; ?>
|
||||
});
|
||||
|
||||
function initMap() {
|
||||
const majuro = { lat: 7.13, lng: 171.38 }; // Default center for Majuro
|
||||
const map = new google.maps.Map(document.getElementById('map'), {
|
||||
zoom: 12,
|
||||
center: majuro,
|
||||
});
|
||||
|
||||
window.marker = null;
|
||||
|
||||
map.addListener('click', (e) => {
|
||||
placeMarker(e.latLng, map);
|
||||
});
|
||||
|
||||
function placeMarker(latLng, map) {
|
||||
if (window.marker) {
|
||||
window.marker.setPosition(latLng);
|
||||
} else {
|
||||
window.marker = new google.maps.Marker({
|
||||
position: latLng,
|
||||
map: map,
|
||||
});
|
||||
}
|
||||
document.getElementById('latitude').value = latLng.lat();
|
||||
document.getElementById('longitude').value = latLng.lng();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||