This commit is contained in:
Flatlogic Bot 2025-10-15 14:58:19 +00:00
parent e98192b894
commit 7a0a2165fc
48 changed files with 1723 additions and 182 deletions

41
admin/assign_driver.php Normal file
View 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;
?>

63
admin/drivers.php Normal file
View 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'; ?>

View File

@ -34,6 +34,12 @@ if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== tru
<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>
</ul>
<ul class="navbar-nav ms-auto">
<li class="nav-item">

View File

@ -32,6 +32,10 @@ $stmt = $pdo->query("
");
$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'];
?>
@ -47,6 +51,7 @@ $possible_statuses = ['Pending', 'Confirmed', 'Preparing', 'Out for Delivery', '
<th>Order Date</th>
<th>Status</th>
<th>Update Status</th>
<th>Assign Driver</th>
</tr>
</thead>
<tbody>
@ -72,6 +77,32 @@ $possible_statuses = ['Pending', 'Confirmed', 'Preparing', 'Out for Delivery', '
<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>

119
admin/settings.php Normal file
View 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'; ?>

View 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;
?>

View File

@ -1,29 +1,37 @@
<?php
header('Content-Type: application/json');
session_start();
require_once '../db/config.php';
require_once __DIR__ . '/../db/config.php';
// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
echo json_encode(['error' => 'Unauthorized']);
exit();
echo json_encode(['error' => 'User not authenticated']);
exit;
}
// Check if order_id is provided
if (!isset($_GET['order_id'])) {
echo json_encode(['error' => 'No order ID specified']);
exit();
echo json_encode(['error' => 'Order ID not specified']);
exit;
}
$order_id = $_GET['order_id'];
$user_id = $_SESSION['user_id'];
// Fetch order status, ensuring the user owns the order
$stmt = $db->prepare("SELECT status FROM orders WHERE id = ? AND user_id = ?");
$stmt->execute([$order_id, $user_id]);
$order = $stmt->fetch(PDO::FETCH_ASSOC);
try {
$pdo = db();
if ($order) {
echo json_encode(['status' => $order['status']]);
} else {
echo json_encode(['error' => 'Order not found or permission denied']);
// Fetch the order status, ensuring the order belongs to the logged-in user
$stmt = $pdo->prepare("SELECT status FROM orders WHERE id = ? AND user_id = ?");
$stmt->execute([$order_id, $user_id]);
$order = $stmt->fetch(PDO::FETCH_ASSOC);
if ($order) {
echo json_encode(['status' => ucwords($order['status'])]);
} else {
echo json_encode(['error' => 'Order not found or permission denied']);
}
} catch (PDOException $e) {
echo json_encode(['error' => 'Database error']);
}
?>

44
api/save_location.php Normal file
View File

@ -0,0 +1,44 @@
<?php
session_start();
header('Content-Type: application/json');
// Define the service area bounds for Majuro
const MAJURO_BOUNDS = [
'minLat' => 7.05,
'maxLat' => 7.15,
'minLng' => 171.17,
'maxLng' => 171.39
];
function isWithinMajuro($lat, $lng) {
return $lat >= MAJURO_BOUNDS['minLat'] && $lat <= MAJURO_BOUNDS['maxLat'] &&
$lng >= MAJURO_BOUNDS['minLng'] && $lng <= MAJURO_BOUNDS['maxLng'];
}
$data = json_decode(file_get_contents('php://input'), true);
if (isset($data['lat']) && isset($data['lng'])) {
$lat = filter_var($data['lat'], FILTER_VALIDATE_FLOAT);
$lng = filter_var($data['lng'], FILTER_VALIDATE_FLOAT);
if ($lat === false || $lng === false) {
echo json_encode(['success' => false, 'message' => 'Invalid coordinates provided.']);
exit;
}
if (!isWithinMajuro($lat, $lng)) {
echo json_encode(['success' => false, 'message' => 'The selected location is outside our service area.']);
exit;
}
$_SESSION['delivery_location'] = [
'lat' => $lat,
'lng' => $lng
];
echo json_encode(['success' => true, 'message' => 'Location saved.']);
} else {
echo json_encode(['success' => false, 'message' => 'No coordinates provided.']);
}
?>

View File

@ -879,3 +879,95 @@ footer {
.filter-dropdown:hover .filter-button-icon {
transform: rotate(180deg);
}
.location-actions {
margin-top: 15px;
display: flex;
gap: 10px;
justify-content: center;
}
.location-button {
background-color: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.5);
color: white;
border-radius: 50px;
padding: 10px 20px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s ease;
}
.location-button:hover {
background-color: rgba(255, 255, 255, 0.3);
border-color: var(--white);
}
/* Star Rating Styles */
.rating {
display: inline-block;
font-size: 2rem;
direction: rtl; /* Right-to-left to fill stars from right */
}
.rating input {
display: none;
}
.rating label {
color: #ddd; /* Default empty star color */
cursor: pointer;
padding: 0 0.1em;
transition: color 0.2s;
}
.rating label:hover,
.rating label:hover ~ label,
.rating input:checked ~ label {
color: #FFC107; /* Filled star color */
}
.row {
display: flex;
flex-wrap: wrap;
margin: 0 -15px;
}
.col-md-3 {
width: 25%;
padding: 0 15px;
}
.col-md-9 {
width: 75%;
padding: 0 15px;
}
.form-check {
margin-bottom: 10px;
}
.btn {
padding: 8px 15px;
border-radius: 5px;
text-decoration: none;
cursor: pointer;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #007bff;
color: white;
border: 1px solid #007bff;
}
.btn-secondary {
background-color: #6c757d;
color: white;
border: 1px solid #6c757d;
}
.mt-3 {
margin-top: 1rem;
}
.search-form {
display: flex;
}
.search-bar {
flex-grow: 1;
}

View File

@ -70,24 +70,33 @@ include 'header.php';
<div class="col-md-6 text-end">
<h4>Subtotal: $<?php echo number_format($totalPrice, 2); ?></h4>
<?php
$discount_amount = 0;
$final_total = $totalPrice;
// Fetch settings from the database
$settingsStmt = $pdoconnection->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 = ($totalPrice * $service_fee_percentage) / 100;
$discount_amount = 0;
if (isset($_SESSION['coupon_code']) && isset($_SESSION['discount_percentage'])) {
$discount_percentage = $_SESSION['discount_percentage'];
$discount_amount = ($totalPrice * $discount_percentage) / 100;
$final_total = $totalPrice - $discount_amount;
?>
<h5 class="text-success">Discount (<?php echo htmlspecialchars($_SESSION['coupon_code']); ?> @ <?php echo $discount_percentage; ?>%): -$<?php echo number_format($discount_amount, 2); ?></h5>
<h3>Total: $<?php echo number_format($final_total, 2); ?></h3>
<a href="remove_coupon.php" class="btn btn-danger btn-sm mt-2">Remove Coupon</a>
<?php
} else {
?>
<h3>Total: $<?php echo number_format($final_total, 2); ?></h3>
<?php
}
$final_total = $totalPrice - $discount_amount + $delivery_fee + $service_fee;
?>
<h5>Delivery Fee: $<?php echo number_format($delivery_fee, 2); ?></h5>
<h5>Service Fee (<?php echo htmlspecialchars($service_fee_percentage); ?>%): $<?php echo number_format($service_fee, 2); ?></h5>
<h3>Total: $<?php echo number_format($final_total, 2); ?></h3>
<?php
$_SESSION['total_price'] = $final_total;
$_SESSION['discount_amount'] = $discount_amount;
$_SESSION['subtotal'] = $totalPrice;

View File

@ -25,8 +25,17 @@ $totalPrice = 0;
foreach ($cartItems as $item) {
$totalPrice += $item['price'] * $item['quantity'];
}
$delivery_fee = 5.00;
$totalPriceWithDelivery = $totalPrice + $delivery_fee;
// Fetch settings from the database
$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 = ($totalPrice * $service_fee_percentage) / 100;
$totalPriceWithFees = $totalPrice + $delivery_fee + $service_fee;
include 'header.php';
@ -86,9 +95,13 @@ include 'header.php';
Delivery Fee
<span>$<?php echo number_format($delivery_fee, 2); ?></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
Service Fee (<?php echo htmlspecialchars($service_fee_percentage); ?>%)
<span>$<?php echo number_format($service_fee, 2); ?></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center fw-bold">
Total
<span>$<?php echo number_format($totalPriceWithDelivery, 2); ?></span>
<span>$<?php echo number_format($totalPriceWithFees, 2); ?></span>
</li>
</ul>
</div>
@ -130,7 +143,7 @@ document.addEventListener('DOMContentLoaded', function () {
return actions.order.create({
purchase_units: [{
amount: {
value: '<?php echo number_format($totalPriceWithDelivery, 2, '.', ''); ?>'
value: '<?php echo number_format($totalPriceWithFees, 2, '.', ''); ?>'
}
}]
});

2
driver/footer.php Normal file
View File

@ -0,0 +1,2 @@
</body>
</html>

27
driver/header.php Normal file
View 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>

75
driver/index.php Normal file
View File

@ -0,0 +1,75 @@
<?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();
$order_statuses = ['preparing', 'out for delivery', 'delivered', 'cancelled'];
?>
<main class="container">
<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>
<?php endforeach; ?>
<?php endif; ?>
</div>
</main>
<?php include 'footer.php'; ?>

30
driver/login.php Normal file
View 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'; ?>

49
driver/login_process.php Normal file
View File

@ -0,0 +1,49 @@
<?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'];
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
View File

@ -0,0 +1,7 @@
<?php
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;
?>

View File

@ -0,0 +1,58 @@
<?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 = ['preparing', 'out for delivery', 'delivered', 'cancelled'];
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])) {
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;
}
?>

View 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
View 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'; ?>

35
driver_signup.php Normal file
View File

@ -0,0 +1,35 @@
<?php include 'header.php'; ?>
<main>
<div class="auth-container">
<h1>Become a Driver</h1>
<form action="driver_signup_process.php" method="POST">
<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>
<button type="submit" class="btn-submit">Sign Up</button>
</form>
<div class="form-footer">
<p>Already have an account? <a href="login.php">Log in</a></p>
</div>
</div>
</main>
<?php include 'footer.php'; ?>

47
driver_signup_process.php Normal file
View File

@ -0,0 +1,47 @@
<?php
session_start();
require_once 'db/config.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.');
}
try {
$pdo = db();
// Check if driver already exists
$sql = "SELECT id FROM drivers WHERE email = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$email]);
if ($stmt->fetch()) {
die('Email already exists.');
}
// Insert into drivers table
$password_hash = password_hash($password, PASSWORD_BCRYPT);
$sql = "INSERT INTO drivers (full_name, email, password_hash, phone_number, vehicle_details, approval_status) VALUES (?, ?, ?, ?, ?, 'pending')";
$stmt = $pdo->prepare($sql);
if ($stmt->execute([$full_name, $email, $password_hash, $phone_number, $vehicle_details])) {
// Redirect to a pending approval page
header("Location: driver_pending_approval.php");
exit;
} else {
die("Error: Could not execute the query.");
}
} catch (PDOException $e) {
die("Could not connect to the database: " . $e->getMessage());
}
}
?>

View File

@ -11,6 +11,7 @@ session_start();
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/main.css?v=<?php echo time(); ?>">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
</head>
<body>
@ -67,4 +68,6 @@ session_start();
</div>
</div>
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>" defer></script>

240
index.php
View File

@ -8,6 +8,10 @@
<input type="text" name="search" class="search-bar" placeholder="Search for restaurants..." value="<?= isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '' ?>">
<button type="submit" class="search-button">Search</button>
</form>
<div class="location-actions">
<button class="location-button" id="pin-location">Pin a Location</button>
<button class="location-button" id="my-location">My Location</button>
</div>
</div>
</section>
@ -15,15 +19,16 @@
<div class="row">
<!-- Filter Sidebar -->
<div class="col-md-3">
<h4>Filter by Cuisine</h4>
<h4>Filter Results</h4>
<form action="index.php" method="get" id="filter-form">
<!-- Hidden search field to persist search query -->
<?php if (isset($_GET['search'])): ?>
<?php if (isset($_GET['search'])):
<input type="hidden" name="search" value="<?= htmlspecialchars($_GET['search']) ?>">
<?php endif; ?>
<h5>By Cuisine</h5>
<?php
$cuisine_stmt = $pdo->query("SELECT * FROM cuisines ORDER BY name");
$cuisine_stmt = db()->query("SELECT * FROM cuisines ORDER BY name");
$all_cuisines = $cuisine_stmt->fetchAll(PDO::FETCH_ASSOC);
$selected_cuisines = isset($_GET['cuisines']) && is_array($_GET['cuisines']) ? $_GET['cuisines'] : [];
@ -35,8 +40,20 @@
</label>
</div>
<?php endforeach; ?>
<button type="submit" class="btn btn-primary mt-3">Apply Filter</button>
<a href="index.php" class="btn btn-secondary mt-3">Clear Filter</a>
<h5 class="mt-4">By Rating</h5>
<div class="form-group">
<select name="min_rating" class="form-control">
<option value="">Any Rating</option>
<option value="4" <?= isset($_GET['min_rating']) && $_GET['min_rating'] == 4 ? 'selected' : '' ?>>4 stars & up</option>
<option value="3" <?= isset($_GET['min_rating']) && $_GET['min_rating'] == 3 ? 'selected' : '' ?>>3 stars & up</option>
<option value="2" <?= isset($_GET['min_rating']) && $_GET['min_rating'] == 2 ? 'selected' : '' ?>>2 stars & up</option>
<option value="1" <?= isset($_GET['min_rating']) && $_GET['min_rating'] == 1 ? 'selected' : '' ?>>1 star & up</option>
</select>
</div>
<button type="submit" class="btn btn-primary mt-3">Apply Filters</button>
<a href="index.php" class="btn btn-secondary mt-3">Clear Filters</a>
</form>
</div>
@ -76,9 +93,18 @@
$sql .= " WHERE " . implode(' AND ', $where_clauses);
}
$sql .= " GROUP BY r.id, r.name, r.image_url ORDER BY r.name";
$sql .= " GROUP BY r.id, r.name, r.image_url";
$stmt = $pdo->prepare($sql);
// Add rating filter (HAVING clause)
$selected_min_rating = isset($_GET['min_rating']) && is_numeric($_GET['min_rating']) ? (int)$_GET['min_rating'] : 0;
if ($selected_min_rating > 0) {
$sql .= " HAVING AVG(rt.rating) >= ?";
$params[] = $selected_min_rating;
}
$sql .= " ORDER BY r.name";
$stmt = db()->prepare($sql);
$stmt->execute($params);
$restaurants = $stmt->fetchAll();
@ -88,11 +114,11 @@
foreach ($restaurants as $restaurant) {
// Get cuisines for this restaurant
$cuisine_sql = "SELECT c.name FROM cuisines c JOIN restaurant_cuisines rc ON c.id = rc.cuisine_id WHERE rc.restaurant_id = ?";
$cuisine_stmt = $pdo->prepare($cuisine_sql);
$cuisine_stmt = db()->prepare($cuisine_sql);
$cuisine_stmt->execute([$restaurant['id']]);
$restaurant_cuisines_list = $cuisine_stmt->fetchAll(PDO::FETCH_COLUMN);
echo '<a href="menu.php?id=' . htmlspecialchars($restaurant['id']) . '" class="restaurant-card">';
echo '<a href="menu.php?restaurant_id=' . htmlspecialchars($restaurant['id']) . '" class="restaurant-card">';
echo '<img src="' . htmlspecialchars($restaurant['image_url'] ? $restaurant['image_url'] : 'assets/images/hero.jpg') . '" alt="' . htmlspecialchars($restaurant['name']) . '">';
echo '<div class="restaurant-card-content">';
echo '<h3>' . htmlspecialchars($restaurant['name']) . '</h3>';
@ -118,55 +144,161 @@
</div>
</main>
<style>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
<div id="map-modal" class="modal">
<div class="modal-content">
<span class="close-button">&times;</span>
<h2>Pin Your Location</h2>
<div id="map" style="height: 400px;"></div>
<p id="map-message"></p>
<button id="confirm-location-button">Confirm Location</button>
<button id="cancel-location-button">Cancel</button>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const pinLocationButton = document.getElementById('pin-location');
const myLocationButton = document.getElementById('my-location');
const locationActions = document.querySelector('.location-actions');
const addressDisplay = document.getElementById('address-display');
const mapModal = document.getElementById('map-modal');
const closeModalButton = mapModal.querySelector('.close-button');
const confirmLocationButton = document.getElementById('confirm-location-button');
const cancelLocationButton = document.getElementById('cancel-location-button');
const mapMessage = document.getElementById('map-message');
let map;
let marker;
const majuroBounds = {
minLat: 7.05,
maxLat: 7.15,
minLng: 171.17,
maxLng: 171.39
};
function isWithinMajuro(lat, lng) {
return lat >= majuroBounds.minLat && lat <= majuroBounds.maxLat &&
lng >= majuroBounds.minLng && lng <= majuroBounds.maxLng;
}
.row {
display: flex;
flex-wrap: wrap;
margin: 0 -15px;
function openMapModal(lat, lng) {
mapModal.style.display = 'block';
if (!map) {
map = L.map('map').setView([7.09, 171.28], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
}
map.setView([lat, lng], 14);
if (marker) {
marker.setLatLng([lat, lng]);
} else {
marker = L.marker([lat, lng], { draggable: true }).addTo(map);
}
mapMessage.textContent = '';
}
.col-md-3 {
width: 25%;
padding: 0 15px;
function closeMapModal() {
mapModal.style.display = 'none';
}
.col-md-9 {
width: 75%;
padding: 0 15px;
pinLocationButton.addEventListener('click', function() {
openMapModal(7.09, 171.28);
});
myLocationButton.addEventListener('click', function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
if (isWithinMajuro(lat, lng)) {
saveLocation(lat, lng);
} else {
alert('Sorry, we do not offer services for your current location.');
}
}, function() {
alert('Could not get your location.');
});
} else {
alert('Geolocation is not supported by your browser.');
}
});
closeModalButton.addEventListener('click', closeMapModal);
cancelLocationButton.addEventListener('click', closeMapModal);
confirmLocationButton.addEventListener('click', function() {
const latlng = marker.getLatLng();
if (isWithinMajuro(latlng.lat, latlng.lng)) {
saveLocation(latlng.lat, latlng.lng);
} else {
mapMessage.textContent = 'Sorry, we do not offer services for the location you pinned. Please select a location within Majuro (Rita-Laura).';
mapMessage.style.color = 'red';
}
});
function saveLocation(lat, lng) {
fetch('api/save_location.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ lat: lat, lng: lng })
})
.then(response => response.json())
.then(data => {
if (data.success) {
updateLocationUI(lat, lng);
closeMapModal();
} else {
alert(data.message || 'Could not save location.');
}
});
}
.form-check {
margin-bottom: 10px;
function updateLocationUI(lat, lng) {
locationActions.innerHTML = `
<div class="pinned-location">
<span>Pinned: ${lat.toFixed(4)}, ${lng.toFixed(4)}</span>
<button id="change-location">Change</button>
</div>
`;
addressDisplay.textContent = `Delivery to: ${lat.toFixed(4)}, ${lng.toFixed(4)}`;
document.getElementById('change-location').addEventListener('click', function() {
locationActions.innerHTML = `
<button class="location-button" id="pin-location">Pin a Location</button>
<button class="location-button" id="my-location">My Location</button>
`;
// Re-add event listeners
document.getElementById('pin-location').addEventListener('click', function() { openMapModal(7.09, 171.28); });
document.getElementById('my-location').addEventListener('click', function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
const lat = position.coords.latitude;
const lng = position.coords.longitude;
if (isWithinMajuro(lat, lng)) {
saveLocation(lat, lng);
} else {
alert('Sorry, we do not offer services for your current location.');
}
}, function() {
alert('Could not get your location.');
});
} else {
alert('Geolocation is not supported by your browser.');
}
});
});
}
.btn {
padding: 8px 15px;
border-radius: 5px;
text-decoration: none;
cursor: pointer;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #007bff;
color: white;
border: 1px solid #007bff;
}
.btn-secondary {
background-color: #6c757d;
color: white;
border: 1px solid #6c757d;
}
.mt-3 {
margin-top: 1rem;
}
.search-form {
display: flex;
}
.search-bar {
flex-grow: 1;
}
</style>
// On page load, check if location is in session
<?php if (isset($_SESSION['delivery_location'])):
updateLocationUI(<?php echo $_SESSION['delivery_location']['lat']; ?>, <?php echo $_SESSION['delivery_location']['lng']; ?>);
<?php endif; ?>
});
</script>
<?php include 'footer.php'; ?>

87
leave_review.php Normal file
View File

@ -0,0 +1,87 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure user is logged in
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
include 'header.php';
$db = db();
$user_id = $_SESSION['user_id'];
$order_id = isset($_GET['order_id']) ? (int)$_GET['order_id'] : 0;
$error_message = '';
$order = null;
if ($order_id > 0) {
// 1. Verify the order exists, belongs to the user, and is delivered
$stmt = $db->prepare("SELECT * FROM orders WHERE id = ? AND user_id = ?");
$stmt->execute([$order_id, $user_id]);
$order = $stmt->fetch();
if (!$order) {
$error_message = "This order could not be found or does not belong to you.";
} elseif ($order['status'] !== 'Delivered') {
$error_message = "You can only review orders that have been delivered.";
} else {
// 2. Check if a review already exists for this order
$stmt_rating = $db->prepare("SELECT id FROM ratings WHERE order_id = ?");
$stmt_rating->execute([$order_id]);
if ($stmt_rating->fetch()) {
$error_message = "You have already submitted a review for this order.";
}
}
} else {
$error_message = "No order was specified.";
}
?>
<div class="container mt-5">
<h2>Leave a Review</h2>
<hr>
<?php
if (isset($_SESSION['success_message'])) {
echo '<div class="alert alert-success">' . $_SESSION['success_message'] . '</div>';
unset($_SESSION['success_message']);
}
if (isset($_SESSION['error_message'])) {
echo '<div class="alert alert-danger">' . $_SESSION['error_message'] . '</div>';
unset($_SESSION['error_message']);
}
?>
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo $error_message; ?></div>
<?php else: ?>
<p>You are reviewing Order #<?php echo htmlspecialchars($order_id); ?></p>
<form action="process_review.php" method="POST">
<input type="hidden" name="order_id" value="<?php echo htmlspecialchars($order_id); ?>">
<input type="hidden" name="restaurant_id" value="<?php echo htmlspecialchars($order['restaurant_id']); ?>">
<div class="form-group">
<label><strong>Rating</strong></label>
<div class="rating">
<input type="radio" name="rating" id="star5" value="5" required><label for="star5"></label>
<input type="radio" name="rating" id="star4" value="4"><label for="star4"></label>
<input type="radio" name="rating" id="star3" value="3"><label for="star3"></label>
<input type="radio" name="rating" id="star2" value="2"><label for="star2"></label>
<input type="radio" name="rating" id="star1" value="1"><label for="star1"></label>
</div>
</div>
<div class="form-group">
<label for="review"><strong>Review (optional)</strong></label>
<textarea name="review" id="review" class="form-control" rows="4" placeholder="Tell us about your experience..."></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Review</button>
</form>
<?php endif; ?>
</div>
<?php include 'footer.php'; ?>

View File

@ -23,17 +23,40 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") {
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$user_id = $user['id'];
$session_id = session_id();
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$_SESSION['user_role'] = $user['role'];
// Merge guest cart with user cart
$session_id = session_id();
$merge_sql = "UPDATE cart SET user_id = ?, session_id = NULL WHERE session_id = ?";
$merge_stmt = $pdo->prepare($merge_sql);
$merge_stmt->execute([$user_id, $session_id]);
$merge_stmt->execute([$user['id'], $session_id]);
$_SESSION['user_id'] = $user_id;
$_SESSION['user_name'] = $user['name'];
header("Location: index.php");
if ($user['role'] == 'admin') {
$_SESSION['admin_logged_in'] = true;
header("Location: admin/index.php");
} elseif ($user['role'] == 'driver') {
$driver_sql = "SELECT approval_status FROM drivers WHERE user_id = ?";
$driver_stmt = $pdo->prepare($driver_sql);
$driver_stmt->execute([$user['id']]);
$driver = $driver_stmt->fetch();
if ($driver) {
if ($driver['approval_status'] == 'approved') {
header("Location: driver/index.php");
} elseif ($driver['approval_status'] == 'pending') {
header("Location: driver_pending_approval.php");
} else { // rejected
header("Location: driver_rejected.php");
}
} else {
// This case should ideally not happen if data is consistent
die('Driver profile not found.');
}
} else { // customer
header("Location: index.php");
}
exit;
} else {
die('Invalid email or password.');

130
menu.php
View File

@ -2,53 +2,36 @@
session_start();
require_once 'db/config.php';
$restaurant_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$restaurant_id = isset($_GET['restaurant_id']) ? (int)$_GET['restaurant_id'] : 0;
if ($restaurant_id === 0) {
header('Location: index.php');
exit;
}
// Handle review submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_review'])) {
if (!isset($_SESSION['user_id'])) {
// Redirect to login if not logged in
header('Location: login.php');
exit;
}
$rating = isset($_POST['rating']) ? (int)$_POST['rating'] : 0;
$review = isset($_POST['review']) ? trim($_POST['review']) : '';
$user_id = $_SESSION['user_id'];
if ($rating >= 1 && $rating <= 5) {
try {
$insert_stmt = db()->prepare("INSERT INTO ratings (restaurant_id, user_id, rating, review) VALUES (:restaurant_id, :user_id, :rating, :review)");
$insert_stmt->bindParam(':restaurant_id', $restaurant_id, PDO::PARAM_INT);
$insert_stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
$insert_stmt->bindParam(':rating', $rating, PDO::PARAM_INT);
$insert_stmt->bindParam(':review', $review, PDO::PARAM_STR);
$insert_stmt->execute();
// Redirect to the same page to prevent form resubmission
header("Location: menu.php?id=$restaurant_id&rated=true");
exit;
} catch (PDOException $e) {
$submit_error = "Error submitting your review. Please try again.";
// In a real app, you'd log this error.
}
} else {
$submit_error = "Please select a rating between 1 and 5.";
}
}
require_once 'header.php';
try {
// Fetch restaurant details
$stmt = db()->prepare("SELECT name, image_url, cuisine FROM restaurants WHERE id = :id");
$stmt = db()->prepare("SELECT r.id, r.name, r.image_url, c.name as cuisine_name
FROM restaurants r
LEFT JOIN restaurant_cuisines rc ON r.id = rc.restaurant_id
LEFT JOIN cuisines c ON rc.cuisine_id = c.id
WHERE r.id = :id");
$stmt->bindParam(':id', $restaurant_id, PDO::PARAM_INT);
$stmt->execute();
$restaurant = $stmt->fetch(PDO::FETCH_ASSOC);
$restaurant_data = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!$restaurant_data) {
throw new Exception("Restaurant not found.");
}
$restaurant = [
'id' => $restaurant_data[0]['id'],
'name' => $restaurant_data[0]['name'],
'image_url' => $restaurant_data[0]['image_url'],
'cuisines' => array_column($restaurant_data, 'cuisine_name')
];
// Fetch menu items
$menu_stmt = db()->prepare("SELECT id, name, description, price, image_url FROM menu_items WHERE restaurant_id = :restaurant_id");
@ -63,28 +46,45 @@ try {
$ratings = $ratings_stmt->fetchAll(PDO::FETCH_ASSOC);
$average_rating = 0;
if (count($ratings) > 0) {
$total_rating = 0;
foreach ($ratings as $r) {
$total_rating += $r['rating'];
}
$average_rating = round($total_rating / count($ratings), 1);
$rating_count = count($ratings);
if ($rating_count > 0) {
$total_rating = array_sum(array_column($ratings, 'rating'));
$average_rating = round($total_rating / $rating_count, 1);
}
} catch (PDOException $e) {
echo "<div class='container'><p class='alert alert-danger'>Error fetching restaurant data.</p></div>";
// Check if this restaurant is a favorite for the current user
$is_favorite = false;
if (isset($_SESSION['user_id'])) {
$fav_stmt = db()->prepare("SELECT COUNT(*) FROM favorite_restaurants WHERE user_id = :user_id AND restaurant_id = :restaurant_id");
$fav_stmt->bindParam(':user_id', $_SESSION['user_id'], PDO::PARAM_INT);
$fav_stmt->bindParam(':restaurant_id', $restaurant_id, PDO::PARAM_INT);
$fav_stmt->execute();
$is_favorite = $fav_stmt->fetchColumn() > 0;
}
} catch (Exception $e) {
echo "<div class='container'><p class='alert alert-danger'>" . $e->getMessage() . "</p></div>";
require_once 'footer.php';
exit;
}
?>
<div class="container mt-5">
<?php if ($restaurant): ?>
<div class="row mb-4 align-items-center">
<div class="col-md-8">
<h1 class="display-4"><?php echo htmlspecialchars($restaurant['name']); ?></h1>
<p class="lead text-muted"><?php echo htmlspecialchars($restaurant['cuisine']); ?></p>
<div class="d-flex align-items-center">
<h1 class="display-4 mb-0"><?php echo htmlspecialchars($restaurant['name']); ?></h1>
<?php if (isset($_SESSION['user_id'])) : ?>
<form action="toggle_favorite.php" method="post" class="ms-4">
<input type="hidden" name="restaurant_id" value="<?php echo $restaurant_id; ?>">
<button type="submit" class="btn <?php echo $is_favorite ? 'btn-danger' : 'btn-outline-danger'; ?>">
<?php echo $is_favorite ? '♥ Remove from Favorites' : '♡ Add to Favorites'; ?>
</button>
</form>
<?php endif; ?>
</div>
<p class="lead text-muted"><?php echo htmlspecialchars(implode(', ', $restaurant['cuisines'])); ?></p>
<div class="d-flex align-items-center">
<span class="h4 text-warning me-2"><?php echo $average_rating; ?> ★</span>
<span class="text-muted">(<?php echo count($ratings); ?> reviews)</span>
@ -138,39 +138,17 @@ try {
<div class="col-lg-8 mx-auto">
<h2 class="mb-4">Reviews & Ratings</h2>
<?php if (isset($_SESSION['user_id'])): ?>
<div class="card mb-4">
<div class="card-header">Leave a Review</div>
<div class="card-body">
<?php if (isset($submit_error)): ?>
<div class="alert alert-danger"><?php echo $submit_error; ?></div>
<?php endif; ?>
<?php if (isset($_GET['rated']) && $_GET['rated'] == 'true'): ?>
<div class="alert alert-success">Thank you for your review!</div>
<?php endif; ?>
<form action="menu.php?id=<?php echo $restaurant_id; ?>" method="post">
<div class="mb-3">
<label for="rating" class="form-label">Your Rating</label>
<select class="form-select" id="rating" name="rating" required>
<option value="" disabled selected>Choose a rating...</option>
<option value="5">5 - Excellent</option>
<option value="4">4 - Very Good</option>
<option value="3">3 - Good</option>
<option value="2">2 - Fair</option>
<option value="1">1 - Poor</option>
</select>
</div>
<div class="mb-3">
<label for="review" class="form-label">Your Review</label>
<textarea class="form-control" id="review" name="review" rows="3" placeholder="What did you think?"></textarea>
</div>
<button type="submit" name="submit_review" class="btn btn-success">Submit Review</button>
</form>
</div>
<?php if (isset($_SESSION['user_id']) && $rating_count > 0): ?>
<div class="alert alert-light">
You have already reviewed this restaurant.
</div>
<?php elseif (isset($_SESSION['user_id'])): ?>
<div class="alert alert-info">
<a href="leave_review.php?order_id=ORDER_ID_PLACEHOLDER">Leave a review</a> for a completed order.
</div>
<?php else: ?>
<div class="alert alert-info">
<a href="login.php?redirect_url=<?php echo urlencode($_SERVER['REQUEST_URI']); ?>">Log in</a> to leave a review.
<a href="login.php?redirect_url=<?php echo urlencode($_SERVER['REQUEST_URI']); ?>">Log in</a> to see reviews or leave your own.
</div>
<?php endif; ?>

View File

@ -0,0 +1,6 @@
ALTER TABLE menu_items
ADD COLUMN promotion_id INT,
ADD CONSTRAINT fk_promotion
FOREIGN KEY(promotion_id)
REFERENCES special_promotions(id)
ON DELETE SET NULL;

View File

@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS driver_assignments (
id SERIAL PRIMARY KEY,
order_id INT NOT NULL,
driver_id INT NOT NULL,
assignment_status VARCHAR(50) NOT NULL DEFAULT 'pending', -- pending, accepted, completed
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (driver_id) REFERENCES drivers(id) ON DELETE CASCADE
);

View File

@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS drivers (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
full_name VARCHAR(255) NOT NULL,
phone_number VARCHAR(255) NOT NULL,
vehicle_details TEXT,
approval_status VARCHAR(50) NOT NULL DEFAULT 'pending', -- pending, approved, rejected
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

View File

@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS favorite_restaurants (
user_id INT NOT NULL,
restaurant_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, restaurant_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE
);

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS ratings (
id SERIAL PRIMARY KEY,
order_id INT NOT NULL UNIQUE,
restaurant_id INT NOT NULL,
user_id INT NOT NULL,
rating INT NOT NULL CHECK (rating >= 1 AND rating <= 5),
review TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

View File

@ -0,0 +1,19 @@
CREATE TABLE IF NOT EXISTS settings (
name VARCHAR(255) PRIMARY KEY,
value VARCHAR(255)
);
INSERT INTO settings (name, value) VALUES
('service_fee_percentage', '7'),
('delivery_fee', '1.50'),
('driver_base_pay_tier1_miles', '3'),
('driver_base_pay_tier1_amount', '3'),
('driver_base_pay_tier2_miles', '5'),
('driver_base_pay_tier2_amount', '4'),
('driver_base_pay_tier3_amount', '5'),
('driver_mileage_pay_rate', '0.65'),
('driver_busy_hour_bonus', '2'),
('driver_quest_bonus_amount', '10'),
('driver_quest_bonus_deliveries', '10'),
('driver_quest_bonus_hours', '2')
ON CONFLICT (name) DO UPDATE SET value = EXCLUDED.value;

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS special_promotions (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
discount_type VARCHAR(10) NOT NULL,
discount_value DECIMAL(10, 2) NOT NULL,
start_date TIMESTAMP NOT NULL,
end_date TIMESTAMP NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

View File

@ -0,0 +1,4 @@
ALTER TABLE drivers DROP CONSTRAINT IF EXISTS drivers_user_id_fkey;
ALTER TABLE drivers DROP COLUMN IF EXISTS user_id;
ALTER TABLE drivers ADD COLUMN IF NOT EXISTS email VARCHAR(255) NOT NULL UNIQUE;
ALTER TABLE drivers ADD COLUMN IF NOT EXISTS password_hash VARCHAR(255) NOT NULL;

View File

@ -3,6 +3,8 @@ session_start();
require_once 'db/config.php';
include 'header.php';
echo '<meta http-equiv="refresh" content="30">';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
@ -31,6 +33,11 @@ if (!$order) {
$p_items = $db->prepare("SELECT oi.*, mi.name as item_name FROM order_items oi JOIN menu_items mi ON oi.menu_item_id = mi.id WHERE oi.order_id = ?");
$p_items->execute([$order_id]);
$items = $p_items->fetchAll(PDO::FETCH_ASSOC);
// Fetch assigned driver
$p_driver = $db->prepare("SELECT d.full_name FROM drivers d JOIN driver_assignments da ON d.id = da.driver_id WHERE da.order_id = ?");
$p_driver->execute([$order_id]);
$driver = $p_driver->fetch(PDO::FETCH_ASSOC);
?>
<div class="container mt-5">
@ -43,6 +50,9 @@ $items = $p_items->fetchAll(PDO::FETCH_ASSOC);
<p><strong>Order Date:</strong> <?php echo date("F j, Y, g:i a", strtotime($order['created_at'])); ?></p>
<p><strong>Total:</strong> $<?php echo number_format($order['total_price'], 2); ?></p>
<p><strong>Status:</strong> <?php echo htmlspecialchars($order['status']); ?></p>
<?php if ($driver): ?>
<p><strong>Driver:</strong> <?php echo htmlspecialchars($driver['full_name']); ?></p>
<?php endif; ?>
</div>
</div>

View File

@ -12,7 +12,8 @@ include 'header.php';
$user_id = $_SESSION['user_id'];
$db = db();
$stmt = $db->prepare("SELECT * FROM orders WHERE user_id = ? ORDER BY order_date DESC");
// Fetch user's orders
$stmt = $db->prepare("SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC");
$stmt->execute([$user_id]);
$orders = $stmt->fetchAll();
@ -22,6 +23,17 @@ $orders = $stmt->fetchAll();
<h2>My Order History</h2>
<hr>
<?php
if (isset($_SESSION['success_message'])) {
echo '<div class="alert alert-success">' . $_SESSION['success_message'] . '</div>';
unset($_SESSION['success_message']);
}
if (isset($_SESSION['error_message'])) {
echo '<div class="alert alert-danger">' . $_SESSION['error_message'] . '</div>';
unset($_SESSION['error_message']);
}
?>
<?php if (empty($orders)): ?>
<div class="alert alert-info">You have not placed any orders yet.</div>
<?php else: ?>
@ -39,12 +51,22 @@ $orders = $stmt->fetchAll();
<?php foreach ($orders as $order): ?>
<tr>
<td><?php echo htmlspecialchars($order['id']); ?></td>
<td><?php echo htmlspecialchars(date('F j, Y, g:i a', strtotime($order['order_date']))); ?></td>
<td><?php echo htmlspecialchars(date('F j, Y, g:i a', strtotime($order['created_at']))); ?></td>
<td>$<?php echo htmlspecialchars(number_format($order['total_price'], 2)); ?></td>
<td><?php echo htmlspecialchars(ucfirst($order['status'])); ?></td>
<td>
<a href="order_details.php?id=<?php echo $order['id']; ?>" class="btn btn-primary btn-sm">View Details</a>
<a href="order_status.php?order_id=<?php echo $order['id']; ?>" class="btn btn-info btn-sm">Track Order</a>
<a href="order_status.php?order_id=<?php echo $order['id']; ?>" class="btn btn-info btn-sm">View Details / Track</a>
<?php
// Check if a review has already been submitted for this order
$stmt_rating = $db->prepare("SELECT id FROM ratings WHERE order_id = ?");
$stmt_rating->execute([$order['id']]);
$existing_rating = $stmt_rating->fetch();
// If order is delivered and no review exists, show the 'Leave a Review' link
if ($order['status'] === 'Delivered' && !$existing_rating):
?>
<a href="leave_review.php?order_id=<?php echo $order['id']; ?>" class="btn btn-success btn-sm mt-1">Leave a Review</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>

View File

@ -16,9 +16,10 @@ if (!isset($_GET['order_id'])) {
$order_id = $_GET['order_id'];
$user_id = $_SESSION['user_id'];
$pdo = db();
// Fetch order details to ensure the user owns this order
$p_order = $db->prepare("SELECT o.*, r.name as restaurant_name FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND o.user_id = ?");
$p_order = $pdo->prepare("SELECT o.*, r.name as restaurant_name FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND o.user_id = ?");
$p_order->execute([$order_id, $user_id]);
$order = $p_order->fetch(PDO::FETCH_ASSOC);
@ -29,7 +30,7 @@ if (!$order) {
}
// Fetch order items
$p_items = $db->prepare("SELECT oi.*, mi.name as item_name FROM order_items oi JOIN menu_items mi ON oi.menu_item_id = mi.id WHERE oi.order_id = ?");
$p_items = $pdo->prepare("SELECT oi.*, mi.name as item_name FROM order_items oi JOIN menu_items mi ON oi.menu_item_id = mi.id WHERE oi.order_id = ?");
$p_items->execute([$order_id]);
$items = $p_items->fetchAll(PDO::FETCH_ASSOC);
@ -44,7 +45,7 @@ $items = $p_items->fetchAll(PDO::FETCH_ASSOC);
<p><strong>Restaurant:</strong> <?php echo htmlspecialchars($order['restaurant_name']); ?></p>
<p><strong>Order Date:</strong> <?php echo date("F j, Y, g:i a", strtotime($order['created_at'])); ?></p>
<div id="order-status-container">
<p><strong>Status:</strong> <span class="badge bg-primary fs-6" id="order-status"><?php echo htmlspecialchars($order['status']); ?></span></p>
<p><strong>Status:</strong> <span class="badge bg-primary fs-6" id="order-status"><?php echo htmlspecialchars(ucwords($order['status'])); ?></span></p>
</div>
<div class="progress" style="height: 25px;">
<div id="progress-bar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
@ -80,8 +81,8 @@ document.addEventListener('DOMContentLoaded', function() {
const statusToProgress = {
'Pending': 10,
'In Progress': 40,
'Out for Delivery': 75,
'Preparing': 40,
'Out For Delivery': 75,
'Delivered': 100,
'Cancelled': 0
};

77
process_review.php Normal file
View File

@ -0,0 +1,77 @@
<?php
session_start();
require_once 'db/config.php';
// Ensure user is logged in
if (!isset($_SESSION['user_id'])) {
// Not logged in
http_response_code(403);
exit("You must be logged in to submit a review.");
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
// Not a POST request
http_response_code(405);
exit("Invalid request method.");
}
$db = db();
$user_id = $_SESSION['user_id'];
// Get POST data
$order_id = isset($_POST['order_id']) ? (int)$_POST['order_id'] : 0;
$restaurant_id = isset($_POST['restaurant_id']) ? (int)$_POST['restaurant_id'] : 0;
$rating = isset($_POST['rating']) ? (int)$_POST['rating'] : 0;
$review = isset($_POST['review']) ? trim($_POST['review']) : '';
// --- Server-side validation ---
// 1. Basic validation
if ($order_id <= 0 || $restaurant_id <= 0 || $rating < 1 || $rating > 5) {
$_SESSION['error_message'] = "Invalid data provided. Please try again.";
header("Location: leave_review.php?order_id=" . $order_id);
exit();
}
// 2. Security and integrity validation
$stmt = $db->prepare("SELECT id, status FROM orders WHERE id = ? AND user_id = ? AND restaurant_id = ?");
$stmt->execute([$order_id, $user_id, $restaurant_id]);
$order = $stmt->fetch();
if (!$order) {
$_SESSION['error_message'] = "You cannot review this order.";
header("Location: order_history.php");
exit();
} elseif ($order['status'] !== 'Delivered') {
$_SESSION['error_message'] = "You can only review delivered orders.";
header("Location: leave_review.php?order_id=" . $order_id);
exit();
}
// 3. Check if a review already exists
$stmt_rating = $db->prepare("SELECT id FROM ratings WHERE order_id = ?");
$stmt_rating->execute([$order_id]);
if ($stmt_rating->fetch()) {
$_SESSION['error_message'] = "You have already reviewed this order.";
header("Location: order_history.php");
exit();
}
// --- All checks passed, insert into database ---
try {
$stmt_insert = $db->prepare(
"INSERT INTO ratings (order_id, restaurant_id, user_id, rating, review) VALUES (?, ?, ?, ?, ?)"
);
$stmt_insert->execute([$order_id, $restaurant_id, $user_id, $rating, $review]);
$_SESSION['success_message'] = "Thank you for your review!";
header("Location: order_history.php");
exit();
} catch (PDOException $e) {
// Log error properly in a real application
// error_log($e->getMessage());
$_SESSION['error_message'] = "A database error occurred. Please try again later.";
header("Location: leave_review.php?order_id=" . $order_id);
exit();
}

View File

@ -56,6 +56,18 @@ $p_user = $db->prepare("SELECT * FROM users WHERE id = ?");
$p_user->execute([$user_id]);
$user = $p_user->fetch();
// Fetch favorite restaurants
$fav_stmt = $db->prepare("
SELECT r.id, r.name, r.image_url, c.name as cuisine_name
FROM favorite_restaurants fr
JOIN restaurants r ON fr.restaurant_id = r.id
LEFT JOIN restaurant_cuisines rc ON r.id = rc.restaurant_id
LEFT JOIN cuisines c ON rc.cuisine_id = c.id
WHERE fr.user_id = ?
GROUP BY r.id
");
$fav_stmt->execute([$user_id]);
$favorite_restaurants = $fav_stmt->fetchAll();
?>
@ -121,6 +133,33 @@ $user = $p_user->fetch();
<div class="mt-5">
<a href="order_history.php" class="btn btn-secondary">View Order History</a>
</div>
<div class="row mt-5">
<div class="col-12">
<h2>My Favorite Restaurants</h2>
<hr>
<?php if ($favorite_restaurants): ?>
<div class="row">
<?php foreach ($favorite_restaurants as $fav_restaurant): ?>
<div class="col-md-4 mb-4">
<div class="card h-100">
<a href="menu.php?restaurant_id=<?php echo $fav_restaurant['id']; ?>">
<img src="<?php echo htmlspecialchars($fav_restaurant['image_url'] ? $fav_restaurant['image_url'] : 'https://via.placeholder.com/300x200'); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($fav_restaurant['name']); ?>" style="height: 200px; object-fit: cover;">
</a>
<div class="card-body d-flex flex-column">
<h5 class="card-title"><?php echo htmlspecialchars($fav_restaurant['name']); ?></h5>
<p class="card-text text-muted"><?php echo htmlspecialchars($fav_restaurant['cuisine_name']); ?></p>
<a href="menu.php?restaurant_id=<?php echo $fav_restaurant['id']; ?>" class="btn btn-primary mt-auto">View Menu</a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<p>You haven't added any favorite restaurants yet. <a href="index.php">Explore restaurants</a> to find some!</p>
<?php endif; ?>
</div>
</div>
</div>
<?php include 'footer.php'; ?>

View File

@ -26,16 +26,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
$description = $_POST['description'] ?? '';
$price = $_POST['price'] ?? '';
$promotion_id = $_POST['promotion_id'] ?? null;
if ($name && $price) {
$stmt = $pdo->prepare("INSERT INTO menu_items (restaurant_id, name, description, price) VALUES (?, ?, ?, ?)");
$stmt->execute([$restaurant_id, $name, $description, $price]);
$stmt = $pdo->prepare("INSERT INTO menu_items (restaurant_id, name, description, price, promotion_id) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$restaurant_id, $name, $description, $price, $promotion_id]);
header('Location: menu.php');
exit;
} else {
$error = "Name and price are required.";
}
}
$stmt = $pdo->prepare("SELECT * FROM special_promotions");
$stmt->execute();
$promotions = $stmt->fetchAll();
?>
<div class="container mt-4">
@ -58,6 +64,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<label for="price" class="form-label">Price</label>
<input type="number" step="0.01" class="form-control" id="price" name="price" required>
</div>
<div class="mb-3">
<label for="promotion_id" class="form-label">Promotion</label>
<select class="form-control" id="promotion_id" name="promotion_id">
<option value="">None</option>
<?php foreach ($promotions as $promotion): ?>
<option value="<?= $promotion['id'] ?>"><?= htmlspecialchars($promotion['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">Add Item</button>
<a href="menu.php" class="btn btn-secondary">Cancel</a>
</form>

View File

@ -0,0 +1,61 @@
<?php
require_once 'header.php';
require_once '../db/config.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'];
$description = $_POST['description'];
$discount_type = $_POST['discount_type'];
$discount_value = $_POST['discount_value'];
$start_date = $_POST['start_date'];
$end_date = $_POST['end_date'];
$is_active = isset($_POST['is_active']) ? 1 : 0;
$stmt = db()->prepare("INSERT INTO special_promotions (name, description, discount_type, discount_value, start_date, end_date, is_active) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $description, $discount_type, $discount_value, $start_date, $end_date, $is_active]);
header('Location: promotions.php');
exit;
}
?>
<div class="container mt-4">
<h2>Add Promotion</h2>
<form method="post">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" id="description" name="description"></textarea>
</div>
<div class="form-group">
<label for="discount_type">Discount Type</label>
<select class="form-control" id="discount_type" name="discount_type">
<option value="percentage">Percentage</option>
<option value="fixed">Fixed</option>
</select>
</div>
<div class="form-group">
<label for="discount_value">Discount Value</label>
<input type="number" class="form-control" id="discount_value" name="discount_value" step="0.01" required>
</div>
<div class="form-group">
<label for="start_date">Start Date</label>
<input type="datetime-local" class="form-control" id="start_date" name="start_date" required>
</div>
<div class="form-group">
<label for="end_date">End Date</label>
<input type="datetime-local" class="form-control" id="end_date" name="end_date" required>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="is_active" name="is_active" value="1" checked>
<label class="form-check-label" for="is_active">Active</label>
</div>
<button type="submit" class="btn btn-primary">Add Promotion</button>
</form>
</div>
<?php require_once 'footer.php'; ?>

View File

@ -0,0 +1,16 @@
<?php
require_once '../db/config.php';
$promotion_id = $_GET['id'];
// First, update menu_items to remove the promotion_id
$stmt = db()->prepare("UPDATE menu_items SET promotion_id = NULL WHERE promotion_id = ?");
$stmt->execute([$promotion_id]);
// Then, delete the promotion
$stmt = db()->prepare("DELETE FROM special_promotions WHERE id = ?");
$stmt->execute([$promotion_id]);
header('Location: promotions.php');
exit;
?>

View File

@ -42,16 +42,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'] ?? '';
$description = $_POST['description'] ?? '';
$price = $_POST['price'] ?? '';
$promotion_id = $_POST['promotion_id'] ?? null;
if ($name && $price) {
$stmt = $pdo->prepare("UPDATE menu_items SET name = ?, description = ?, price = ? WHERE id = ? AND restaurant_id = ?");
$stmt->execute([$name, $description, $price, $menu_item_id, $restaurant_id]);
$stmt = $pdo->prepare("UPDATE menu_items SET name = ?, description = ?, price = ?, promotion_id = ? WHERE id = ? AND restaurant_id = ?");
$stmt->execute([$name, $description, $price, $promotion_id, $menu_item_id, $restaurant_id]);
header('Location: menu.php');
exit;
} else {
$error = "Name and price are required.";
}
}
$stmt = $pdo->prepare("SELECT * FROM special_promotions");
$stmt->execute();
$promotions = $stmt->fetchAll();
?>
<div class="container mt-4">
@ -74,6 +80,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
<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>
<div class="mb-3">
<label for="promotion_id" class="form-label">Promotion</label>
<select class="form-control" id="promotion_id" name="promotion_id">
<option value="">None</option>
<?php foreach ($promotions as $promotion): ?>
<option value="<?= $promotion['id'] ?>" <?= $item['promotion_id'] == $promotion['id'] ? 'selected' : '' ?>><?= htmlspecialchars($promotion['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">Update Item</button>
<a href="menu.php" class="btn btn-secondary">Cancel</a>
</form>

View File

@ -0,0 +1,67 @@
<?php
require_once 'header.php';
require_once '../db/config.php';
$promotion_id = $_GET['id'];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $_POST['name'];
$description = $_POST['description'];
$discount_type = $_POST['discount_type'];
$discount_value = $_POST['discount_value'];
$start_date = $_POST['start_date'];
$end_date = $_POST['end_date'];
$is_active = isset($_POST['is_active']) ? 1 : 0;
$stmt = db()->prepare("UPDATE special_promotions SET name = ?, description = ?, discount_type = ?, discount_value = ?, start_date = ?, end_date = ?, is_active = ? WHERE id = ?");
$stmt->execute([$name, $description, $discount_type, $discount_value, $start_date, $end_date, $is_active, $promotion_id]);
header('Location: promotions.php');
exit;
}
$stmt = db()->prepare("SELECT * FROM special_promotions WHERE id = ?");
$stmt->execute([$promotion_id]);
$promotion = $stmt->fetch();
?>
<div class="container mt-4">
<h2>Edit Promotion</h2>
<form method="post">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" name="name" value="<?= htmlspecialchars($promotion['name']) ?>" required>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" id="description" name="description"><?= htmlspecialchars($promotion['description']) ?></textarea>
</div>
<div class="form-group">
<label for="discount_type">Discount Type</label>
<select class="form-control" id="discount_type" name="discount_type">
<option value="percentage" <?= $promotion['discount_type'] == 'percentage' ? 'selected' : '' ?>>Percentage</option>
<option value="fixed" <?= $promotion['discount_type'] == 'fixed' ? 'selected' : '' ?>>Fixed</option>
</select>
</div>
<div class="form-group">
<label for="discount_value">Discount Value</label>
<input type="number" class="form-control" id="discount_value" name="discount_value" step="0.01" value="<?= htmlspecialchars($promotion['discount_value']) ?>" required>
</div>
<div class="form-group">
<label for="start_date">Start Date</label>
<input type="datetime-local" class="form-control" id="start_date" name="start_date" value="<?= date('Y-m-d\TH:i', strtotime($promotion['start_date'])) ?>" required>
</div>
<div class="form-group">
<label for="end_date">End Date</label>
<input type="datetime-local" class="form-control" id="end_date" name="end_date" value="<?= date('Y-m-d\TH:i', strtotime($promotion['end_date'])) ?>" required>
</div>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="is_active" name="is_active" value="1" <?= $promotion['is_active'] ? 'checked' : '' ?>>
<label class="form-check-label" for="is_active">Active</label>
</div>
<button type="submit" class="btn btn-primary">Update Promotion</button>
</form>
</div>
<?php require_once 'footer.php'; ?>

View File

@ -31,6 +31,9 @@ if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] !== 'restaurant_own
<li class="nav-item">
<a class="nav-link" href="orders.php">Orders</a>
</li>
<li class="nav-item">
<a class="nav-link" href="promotions.php">Promotions</a>
</li>
</ul>
<ul class="navbar-nav ms-auto">
<li class="nav-item">

47
restaurant/promotions.php Normal file
View File

@ -0,0 +1,47 @@
<?php
require_once 'header.php';
require_once '../db/config.php';
// Fetch promotions for the logged-in restaurant owner
$restaurant_id = $_SESSION['restaurant_id'];
$stmt = db()->prepare("SELECT sp.* FROM special_promotions sp JOIN menu_items mi ON sp.id = mi.promotion_id JOIN restaurants r ON mi.restaurant_id = r.id WHERE r.id = ?");
$stmt->execute([$restaurant_id]);
$promotions = $stmt->fetchAll();
?>
<div class="container mt-4">
<h2>Manage Promotions</h2>
<a href="add_promotion.php" class="btn btn-primary mb-3">Add Promotion</a>
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Discount</th>
<th>Start Date</th>
<th>End Date</th>
<th>Active</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($promotions as $promotion): ?>
<tr>
<td><?= htmlspecialchars($promotion['name']) ?></td>
<td><?= htmlspecialchars($promotion['description']) ?></td>
<td><?= htmlspecialchars($promotion['discount_value']) ?> <?= $promotion['discount_type'] == 'percentage' ? '%' : '' ?></td>
<td><?= htmlspecialchars($promotion['start_date']) ?></td>
<td><?= htmlspecialchars($promotion['end_date']) ?></td>
<td><?= $promotion['is_active'] ? 'Yes' : 'No' ?></td>
<td>
<a href="edit_promotion.php?id=<?= $promotion['id'] ?>" class="btn btn-sm btn-info">Edit</a>
<a href="delete_promotion.php?id=<?= $promotion['id'] ?>" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php require_once 'footer.php'; ?>

57
toggle_favorite.php Normal file
View File

@ -0,0 +1,57 @@
<?php
session_start();
require_once 'db/config.php';
// Check if the user is logged in
if (!isset($_SESSION['user_id'])) {
// Redirect to login page if not logged in
header('Location: login.php');
exit;
}
// Check if the request method is POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Get the restaurant ID from the POST data
$restaurant_id = isset($_POST['restaurant_id']) ? (int)$_POST['restaurant_id'] : 0;
$user_id = $_SESSION['user_id'];
if ($restaurant_id > 0) {
try {
$db = db();
// Check if the restaurant is already a favorite
$stmt = $db->prepare("SELECT COUNT(*) FROM favorite_restaurants WHERE user_id = :user_id AND restaurant_id = :restaurant_id");
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
$stmt->bindParam(':restaurant_id', $restaurant_id, PDO::PARAM_INT);
$stmt->execute();
$is_favorite = $stmt->fetchColumn() > 0;
if ($is_favorite) {
// Remove from favorites
$delete_stmt = $db->prepare("DELETE FROM favorite_restaurants WHERE user_id = :user_id AND restaurant_id = :restaurant_id");
$delete_stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
$delete_stmt->bindParam(':restaurant_id', $restaurant_id, PDO::PARAM_INT);
$delete_stmt->execute();
$_SESSION['alert'] = ['type' => 'success', 'message' => 'Restaurant removed from your favorites.'];
} else {
// Add to favorites
$insert_stmt = $db->prepare("INSERT INTO favorite_restaurants (user_id, restaurant_id) VALUES (:user_id, :restaurant_id)");
$insert_stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
$insert_stmt->bindParam(':restaurant_id', $restaurant_id, PDO::PARAM_INT);
$insert_stmt->execute();
$_SESSION['alert'] = ['type' => 'success', 'message' => 'Restaurant added to your favorites!'];
}
} catch (PDOException $e) {
// Handle database errors
$_SESSION['alert'] = ['type' => 'danger', 'message' => 'Database error: ' . $e->getMessage()];
}
}
// Redirect back to the menu page
header('Location: menu.php?restaurant_id=' . $restaurant_id);
exit;
} else {
// If not a POST request, redirect to the homepage
header('Location: index.php');
exit;
}