V14
This commit is contained in:
parent
5fc6fe4ad4
commit
adf8c9c972
@ -3,35 +3,43 @@ header('Content-Type: application/json');
|
||||
session_start();
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// Check if user is logged in
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
echo json_encode(['error' => 'User not authenticated']);
|
||||
exit;
|
||||
}
|
||||
$order_id = $_GET['order_id'] ?? null;
|
||||
$token = $_GET['token'] ?? null;
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
|
||||
// Check if order_id is provided
|
||||
if (!isset($_GET['order_id'])) {
|
||||
if (!$order_id) {
|
||||
echo json_encode(['error' => 'Order ID not specified']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$order_id = $_GET['order_id'];
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$status = null;
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// Fetch the order status, ensuring the order belongs to the logged-in user
|
||||
if ($user_id) {
|
||||
$stmt = $pdo->prepare("SELECT status FROM orders WHERE id = ? AND user_id = ?");
|
||||
$stmt->execute([$order_id, $user_id]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($result) {
|
||||
$status = $result['status'];
|
||||
}
|
||||
} elseif ($token) {
|
||||
$stmt = $pdo->prepare("SELECT status FROM orders WHERE id = ? AND guest_token = ?");
|
||||
$stmt->execute([$order_id, $token]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($result) {
|
||||
$status = $result['status'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($order) {
|
||||
echo json_encode(['status' => ucwords($order['status'])]);
|
||||
if ($status) {
|
||||
echo json_encode(['status' => ucwords($status)]);
|
||||
} else {
|
||||
echo json_encode(['error' => 'Order not found or permission denied']);
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['error' => 'Database error']);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Database connection error']);
|
||||
}
|
||||
?>
|
||||
@ -264,17 +264,17 @@ footer {
|
||||
/* Restaurant Menu Page */
|
||||
.restaurant-hero-menu {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
height: 350px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding: 2rem;
|
||||
padding: 3rem;
|
||||
color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
border-radius: 0 0 var(--border-radius) var(--border-radius);
|
||||
overflow: hidden;
|
||||
margin-bottom: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.restaurant-hero-menu::after {
|
||||
@ -284,7 +284,7 @@ footer {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0) 100%);
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.1) 100%);
|
||||
}
|
||||
|
||||
.restaurant-hero-menu-content {
|
||||
@ -293,23 +293,23 @@ footer {
|
||||
}
|
||||
|
||||
.restaurant-hero-menu h1 {
|
||||
font-size: 2.5rem;
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5rem;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.6);
|
||||
text-shadow: 0 2px 8px rgba(0,0,0,0.7);
|
||||
}
|
||||
|
||||
.restaurant-hero-menu p {
|
||||
font-size: 1rem;
|
||||
font-size: 1.1rem;
|
||||
margin: 0.25rem 0;
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
|
||||
text-shadow: 0 1px 5px rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
/* Menu Grid */
|
||||
.menu-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.menu-item-card {
|
||||
@ -319,7 +319,8 @@ footer {
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease-in-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.menu-item-card:hover {
|
||||
@ -327,21 +328,23 @@ footer {
|
||||
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.menu-item-card img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
.menu-item-image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: cover;
|
||||
border-radius: 12px;
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
|
||||
.menu-item-card-content {
|
||||
padding: 1.5rem;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.menu-item-card-content h3 {
|
||||
font-size: 1.15rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
@ -357,9 +360,50 @@ footer {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
/* Reviews Section */
|
||||
.reviews-section {
|
||||
background-color: var(--white);
|
||||
padding: 2rem;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
position: sticky;
|
||||
top: 120px; /* Adjust based on header height */
|
||||
}
|
||||
|
||||
.review-card {
|
||||
border-bottom: 1px solid var(--light-gray);
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.review-card:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.review-card:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.review-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.review-header strong {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.review-text {
|
||||
font-style: italic;
|
||||
color: var(--dark-gray);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
/* Rating Section on Menu Page */
|
||||
.rating-form-container {
|
||||
background-color: var(--white);
|
||||
@ -1030,3 +1074,475 @@ footer {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Categories Section */
|
||||
.categories-section {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.category-card {
|
||||
position: relative;
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
height: 150px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
text-decoration: none;
|
||||
color: var(--white);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.category-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.category-card img {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: 1;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.category-card:hover img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.category-card-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 60%);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.category-card h3 {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
text-shadow: 0 2px 4px rgba(0,0,0,0.5);
|
||||
}
|
||||
|
||||
/* New Checkout Styles */
|
||||
.checkout-container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background-color: var(--white);
|
||||
}
|
||||
|
||||
.checkout-main {
|
||||
flex-grow: 1;
|
||||
padding: 2rem 4rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.checkout-header {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.checkout-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.step-title {
|
||||
font-size: 1.75rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#delivery-form .form-group {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#delivery-form .form-control {
|
||||
padding: 1rem;
|
||||
font-size: 1rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--medium-gray);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
background-color: var(--coral);
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #ff4f4f;
|
||||
}
|
||||
|
||||
.payment-methods {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.payment-method-card {
|
||||
border: 1px solid var(--medium-gray);
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
|
||||
.payment-method-card:has(input:checked) {
|
||||
border-color: var(--coral);
|
||||
box-shadow: 0 0 0 2px var(--coral);
|
||||
}
|
||||
|
||||
.payment-method-card label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.payment-method-card input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.payment-method-card svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
#paypal-button-container {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
background-color: transparent;
|
||||
color: var(--dark-gray);
|
||||
border: 1px solid var(--medium-gray);
|
||||
cursor: pointer;
|
||||
margin-top: 1rem;
|
||||
transition: background-color 0.3s, border-color 0.3s;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: var(--light-gray);
|
||||
border-color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.checkout-summary {
|
||||
width: 450px;
|
||||
background-color: var(--off-white);
|
||||
padding: 2rem;
|
||||
border-left: 1px solid var(--light-gray);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.checkout-summary h4 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.summary-items {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.item-price {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.summary-total {
|
||||
border-top: 1px solid var(--medium-gray);
|
||||
padding-top: 1.5rem;
|
||||
}
|
||||
|
||||
.summary-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.summary-line.discount {
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.summary-line.total {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* Order Confirmation Page Refined */
|
||||
.card.shadow-sm {
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.card-header.bg-success {
|
||||
background-color: var(--coral) !important;
|
||||
border-bottom: none;
|
||||
border-radius: var(--border-radius) var(--border-radius) 0 0;
|
||||
}
|
||||
|
||||
.card-header h2 {
|
||||
font-size: 1.75rem;
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.card-body .lead {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.card-body h5 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-color);
|
||||
border-bottom: 2px solid var(--light-gray);
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.badge.bg-warning {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.5em 0.75em;
|
||||
background-color: var(--turquoise) !important;
|
||||
color: var(--text-color) !important;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-color: var(--light-gray);
|
||||
}
|
||||
|
||||
.list-group-item span {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.list-group-item.fw-bold {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.order-confirmation-actions .btn {
|
||||
margin: 0 0.5rem;
|
||||
padding: 0.8rem 1.5rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.9rem;
|
||||
border-radius: 50px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.order-confirmation-actions .btn-primary {
|
||||
background-color: var(--coral);
|
||||
border-color: var(--coral);
|
||||
}
|
||||
|
||||
.order-confirmation-actions .btn-primary:hover {
|
||||
background-color: #ff4f4f;
|
||||
border-color: #ff4f4f;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.order-confirmation-actions .btn-secondary {
|
||||
background-color: var(--off-white);
|
||||
border-color: var(--medium-gray);
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
.order-confirmation-actions .btn-secondary:hover {
|
||||
background-color: var(--light-gray);
|
||||
border-color: var(--dark-gray);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Order Status Page */
|
||||
.order-status-container {
|
||||
max-width: 800px;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
background-color: var(--white);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.order-status-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2.5rem;
|
||||
border-bottom: 1px solid var(--light-gray);
|
||||
padding-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.order-status-header h1 {
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.order-status-header p {
|
||||
font-size: 1.1rem;
|
||||
color: var(--dark-gray);
|
||||
}
|
||||
|
||||
#order-status-timeline {
|
||||
position: relative;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding-left: 60px; /* Space for icon and line */
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 24px; /* Center the line */
|
||||
top: 50px;
|
||||
width: 2px;
|
||||
height: calc(100% - 50px);
|
||||
background-color: var(--light-gray);
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
|
||||
.timeline-item:last-child::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--light-gray);
|
||||
color: var(--medium-gray);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
z-index: 1;
|
||||
border: 4px solid var(--off-white);
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.timeline-content h5 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.25rem;
|
||||
transition: color 0.5s ease;
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-content p {
|
||||
font-size: 0.95rem;
|
||||
color: var(--dark-gray);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* --- Timeline States --- */
|
||||
|
||||
/* Default (Not yet active) */
|
||||
.timeline-item:not(.timeline-complete):not(.timeline-active) .timeline-icon {
|
||||
background-color: var(--light-gray);
|
||||
color: var(--medium-gray);
|
||||
}
|
||||
|
||||
/* Active State */
|
||||
.timeline-active .timeline-icon {
|
||||
background-color: var(--turquoise);
|
||||
color: var(--white);
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 0 15px rgba(64, 224, 208, 0.7);
|
||||
}
|
||||
|
||||
.timeline-active h5 {
|
||||
color: var(--turquoise);
|
||||
}
|
||||
|
||||
/* Complete State */
|
||||
.timeline-complete::before {
|
||||
background-color: var(--coral);
|
||||
}
|
||||
|
||||
.timeline-complete .timeline-icon {
|
||||
background-color: var(--coral);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
/* Cancelled State */
|
||||
.timeline-cancelled .timeline-icon {
|
||||
background-color: var(--dark-gray);
|
||||
color: var(--white);
|
||||
}
|
||||
|
||||
.timeline-cancelled h5 {
|
||||
color: var(--dark-gray);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.order-status-footer {
|
||||
text-align: center;
|
||||
margin-top: 2.5rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid var(--light-gray);
|
||||
}
|
||||
|
||||
.order-status-footer p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
262
checkout.php
262
checkout.php
@ -3,16 +3,26 @@ session_start();
|
||||
require_once 'db/config.php';
|
||||
require_once 'includes/api_keys.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$userId = $_SESSION['user_id'];
|
||||
$is_guest = !isset($_SESSION['user_id']);
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$session_id = session_id();
|
||||
$pdo = db();
|
||||
|
||||
$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', $userId);
|
||||
$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);
|
||||
|
||||
@ -21,144 +31,216 @@ if (empty($cartItems)) {
|
||||
exit();
|
||||
}
|
||||
|
||||
$totalPrice = 0;
|
||||
$subtotal = 0;
|
||||
foreach ($cartItems as $item) {
|
||||
$totalPrice += $item['price'] * $item['quantity'];
|
||||
$subtotal += $item['price'] * $item['quantity'];
|
||||
}
|
||||
|
||||
// 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 = ($subtotal * $service_fee_percentage) / 100;
|
||||
|
||||
$service_fee = ($totalPrice * $service_fee_percentage) / 100;
|
||||
$totalPriceWithFees = $totalPrice + $delivery_fee + $service_fee;
|
||||
|
||||
$discount_amount = $_SESSION['discount_amount'] ?? 0;
|
||||
$totalPrice = $subtotal + $delivery_fee + $service_fee - $discount_amount;
|
||||
|
||||
$_SESSION['total_price'] = $totalPrice;
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<script src="https://www.paypal.com/sdk/js?client-id=<?php echo $paypalClientId; ?>¤cy=USD"></script>
|
||||
<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="container mt-5">
|
||||
<h2 class="text-center mb-4">Checkout</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<h4>Delivery Information</h4>
|
||||
<form id="payment-form" action="create_stripe_session.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Full Name</label>
|
||||
<input type="text" class="form-control" id="name" name="name" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="address" class="form-label">Address</label>
|
||||
<input type="text" class="form-control" id="address" name="address" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">Phone Number</label>
|
||||
<input type="text" class="form-control" id="phone" name="phone" required>
|
||||
<div class="checkout-container">
|
||||
<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>
|
||||
|
||||
<h4 class="mt-4">Payment Method</h4>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="payment_method" id="stripe-radio" value="stripe" checked>
|
||||
<label class="form-check-label" for="stripe-radio">
|
||||
Pay with Credit Card (Stripe)
|
||||
</label>
|
||||
<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>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="payment_method" id="paypal-radio" value="paypal">
|
||||
<label class="form-check-label" for="paypal-radio">
|
||||
Pay with PayPal
|
||||
</label>
|
||||
<?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>
|
||||
|
||||
<button id="stripe-button" type="submit" class="btn btn-primary mt-3">Proceed to Payment</button>
|
||||
<?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 id="paypal-button-container" class="mt-3" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<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>
|
||||
|
||||
<div class="checkout-summary">
|
||||
<h4>Order Summary</h4>
|
||||
<ul class="list-group mb-3">
|
||||
<div class="summary-items">
|
||||
<?php foreach ($cartItems as $item): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?php echo htmlspecialchars($item['name']); ?> (x<?php echo $item['quantity']; ?>)
|
||||
<span>$<?php echo number_format($item['price'] * $item['quantity'], 2); ?></span>
|
||||
</li>
|
||||
<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; ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Delivery Fee
|
||||
</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>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
Service Fee (<?php echo htmlspecialchars($service_fee_percentage); ?>%)
|
||||
</div>
|
||||
<div class="summary-line">
|
||||
<span>Service Fee</span>
|
||||
<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($totalPriceWithFees, 2); ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
</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 form = document.getElementById('payment-form');
|
||||
const stripeButton = document.getElementById('stripe-button');
|
||||
const paypalButtonContainer = document.getElementById('paypal-button-container');
|
||||
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');
|
||||
|
||||
function togglePaymentMethod() {
|
||||
if (paypalRadio.checked) {
|
||||
stripeButton.style.display = 'none';
|
||||
paypalButtonContainer.style.display = 'block';
|
||||
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', togglePaymentMethod);
|
||||
paypalRadio.addEventListener('change', togglePaymentMethod);
|
||||
stripeRadio.addEventListener('change', togglePaymentButtons);
|
||||
paypalRadio.addEventListener('change', togglePaymentButtons);
|
||||
|
||||
// Initial check
|
||||
togglePaymentMethod();
|
||||
document.querySelectorAll('.payment-method-card').forEach(card => {
|
||||
card.addEventListener('click', () => {
|
||||
card.querySelector('input[type="radio"]').checked = true;
|
||||
togglePaymentButtons();
|
||||
});
|
||||
});
|
||||
|
||||
togglePaymentButtons();
|
||||
|
||||
// PayPal integration
|
||||
paypal.Buttons({
|
||||
createOrder: function(data, actions) {
|
||||
// Basic validation
|
||||
if (!document.getElementById('name').value || !document.getElementById('address').value || !document.getElementById('phone').value) {
|
||||
alert('Please fill out the delivery information before proceeding.');
|
||||
return false;
|
||||
}
|
||||
return actions.order.create({
|
||||
purchase_units: [{
|
||||
amount: {
|
||||
value: '<?php echo number_format($totalPriceWithFees, 2, '.', ''); ?>'
|
||||
value: '<?php echo number_format($totalPrice, 2, '.', ''); ?>'
|
||||
}
|
||||
}]
|
||||
});
|
||||
},
|
||||
onApprove: function(data, actions) {
|
||||
// Capture delivery info and submit
|
||||
const name = document.getElementById('name').value;
|
||||
const address = document.getElementById('address').value;
|
||||
const phone = document.getElementById('phone').value;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('orderID', data.orderID);
|
||||
formData.append('name', name);
|
||||
formData.append('address', address);
|
||||
formData.append('phone', phone);
|
||||
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',
|
||||
@ -169,7 +251,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
alert(details.error);
|
||||
window.location.href = 'payment-cancel.php';
|
||||
} else {
|
||||
window.location.href = 'order_confirmation.php';
|
||||
window.location.href = 'order_confirmation.php?order_id=' + details.order_id;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -9,13 +9,32 @@ require_once 'includes/api_keys.php';
|
||||
$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();
|
||||
}
|
||||
|
||||
$checkout_session = \Stripe\Checkout\Session::create([
|
||||
$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' => [
|
||||
@ -30,10 +49,13 @@ $checkout_session = \Stripe\Checkout\Session::create([
|
||||
'mode' => 'payment',
|
||||
'success_url' => 'http://localhost:8080/payment-success.php?session_id={CHECKOUT_SESSION_ID}',
|
||||
'cancel_url' => 'http://localhost:8080/payment-cancel.php',
|
||||
'metadata' => [
|
||||
'user_id' => $user_id,
|
||||
'coupon_id' => $coupon_id
|
||||
]
|
||||
]);
|
||||
'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);
|
||||
@ -13,6 +13,7 @@ function db() {
|
||||
$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;
|
||||
|
||||
78
index.php
78
index.php
@ -2,26 +2,47 @@
|
||||
<?php include 'hero.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<form action="index.php" method="get" id="filter-form">
|
||||
<!-- Hidden search field to persist search query -->
|
||||
<input type="hidden" name="search" value="<?= isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '' ?>">
|
||||
|
||||
<h2 class="page-title">Explore Cuisines</h2>
|
||||
<div class="cuisine-carousel">
|
||||
<section class="categories-section">
|
||||
<h2 class="page-title">Discover by Category</h2>
|
||||
<div class="category-grid">
|
||||
<?php
|
||||
$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'] : [];
|
||||
$cuisines = $cuisine_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
// Placeholder images - we'll make these dynamic later
|
||||
$cuisine_images = [
|
||||
'American' => 'assets/images/pexels/1639557.jpg',
|
||||
'Asian' => 'assets/images/pexels/2347311.jpg',
|
||||
'BBQ' => 'assets/images/pexels/161963.jpg',
|
||||
'Breakfast' => 'assets/images/pexels/376464.jpg',
|
||||
'Cafe' => 'assets/images/pexels/312418.jpg',
|
||||
'Dessert' => 'assets/images/pexels/1099680.jpg',
|
||||
'Fast Food' => 'assets/images/pexels/1633578.jpg',
|
||||
'Healthy' => 'assets/images/pexels/1640777.jpg',
|
||||
'Indian' => 'assets/images/pexels/958545.jpg',
|
||||
'Italian' => 'assets/images/pexels/1260968.jpg',
|
||||
'Mexican' => 'assets/images/pexels/461198.jpg',
|
||||
'Pizza' => 'assets/images/pexels/1146760.jpg',
|
||||
'Seafood' => 'assets/images/pexels/1639565.jpg',
|
||||
'Vegetarian' => 'assets/images/pexels/1143754.jpg',
|
||||
];
|
||||
|
||||
foreach ($all_cuisines as $cuisine): ?>
|
||||
<div class="cuisine-card">
|
||||
<input type="checkbox" name="cuisines[]" value="<?= $cuisine['id'] ?>" id="cuisine-<?= $cuisine['id'] ?>" <?= in_array($cuisine['id'], $selected_cuisines) ? 'checked' : '' ?> onchange="this.form.submit()">
|
||||
<label for="cuisine-<?= $cuisine['id'] ?>">
|
||||
<?= htmlspecialchars($cuisine['name']) ?>
|
||||
</label>
|
||||
</div>
|
||||
foreach ($cuisines as $cuisine):
|
||||
$image_url = $cuisine_images[$cuisine['name']] ?? 'https://picsum.photos/600?random=' . $cuisine['id'];
|
||||
?>
|
||||
<a href="index.php?cuisine=<?= $cuisine['id'] ?>" class="category-card">
|
||||
<img src="<?= htmlspecialchars($image_url) ?>" alt="<?= htmlspecialchars($cuisine['name']) ?>">
|
||||
<div class="category-card-overlay"></div>
|
||||
<h3><?= htmlspecialchars($cuisine['name']) ?></h3>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<form action="index.php" method="get" id="filter-form">
|
||||
<input type="hidden" name="search" value="<?= isset($_GET['search']) ? htmlspecialchars($_GET['search']) : '' ?>">
|
||||
<?php if (isset($_GET['cuisine'])): ?>
|
||||
<input type="hidden" name="cuisine" value="<?= htmlspecialchars($_GET['cuisine']) ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="filter-bar-new">
|
||||
<div class="form-group">
|
||||
@ -44,7 +65,19 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h2 class="page-title mt-4">All Restaurants</h2>
|
||||
<?php
|
||||
$page_title = "All Restaurants";
|
||||
if (isset($_GET['cuisine']) && !empty($_GET['cuisine'])) {
|
||||
$cuisine_id = $_GET['cuisine'];
|
||||
$cuisine_stmt = db()->prepare("SELECT name FROM cuisines WHERE id = ?");
|
||||
$cuisine_stmt->execute([$cuisine_id]);
|
||||
$cuisine_name = $cuisine_stmt->fetchColumn();
|
||||
if ($cuisine_name) {
|
||||
$page_title = htmlspecialchars($cuisine_name) . " Restaurants";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<h2 class="page-title mt-4"><?= $page_title ?></h2>
|
||||
<section class="restaurant-list">
|
||||
<div class="restaurant-grid" id="restaurant-grid">
|
||||
<?php
|
||||
@ -55,7 +88,7 @@
|
||||
$params = [];
|
||||
|
||||
// Join with restaurant_cuisines if filtering by cuisine
|
||||
if (!empty($selected_cuisines)) {
|
||||
if (isset($_GET['cuisine']) && !empty($_GET['cuisine'])) {
|
||||
$sql .= " JOIN restaurant_cuisines rc ON r.id = rc.restaurant_id";
|
||||
}
|
||||
|
||||
@ -68,10 +101,9 @@
|
||||
}
|
||||
|
||||
// Append cuisine filter
|
||||
if (!empty($selected_cuisines)) {
|
||||
$placeholders = implode(',', array_fill(0, count($selected_cuisines), '?'));
|
||||
$where_clauses[] = "rc.cuisine_id IN ($placeholders)";
|
||||
$params = array_merge($params, $selected_cuisines);
|
||||
if (isset($_GET['cuisine']) && !empty($_GET['cuisine'])) {
|
||||
$where_clauses[] = "rc.cuisine_id = ?";
|
||||
$params[] = $_GET['cuisine'];
|
||||
}
|
||||
|
||||
// Append "Open Now" filter
|
||||
@ -291,8 +323,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
// 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; ?>
|
||||
echo "updateLocationUI(" . $_SESSION['delivery_location']['lat'] . ", " . $_SESSION['delivery_location']['lng'] . ");";
|
||||
endif; ?>
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
108
menu.php
108
menu.php
@ -69,111 +69,85 @@ try {
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<?php if ($restaurant): ?>
|
||||
<div class="row mb-4 align-items-center">
|
||||
<div class="col-md-8">
|
||||
<div class="restaurant-hero-menu" style="background-image: url('<?php echo htmlspecialchars($restaurant['image_url']); ?>');">
|
||||
<div class="restaurant-hero-menu-content">
|
||||
<h1><?php echo htmlspecialchars($restaurant['name']); ?></h1>
|
||||
<p><?php echo htmlspecialchars(implode(', ', $restaurant['cuisines'])); ?></p>
|
||||
<div class="d-flex align-items-center">
|
||||
<h1 class="display-4 mb-0"><?php echo htmlspecialchars($restaurant['name']); ?></h1>
|
||||
<span class="h4 text-warning me-2"><?php echo $average_rating; ?> ★</span>
|
||||
<span class="text-white">(<?php echo count($ratings); ?> reviews)</span>
|
||||
<?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 type="submit" class="btn <?php echo $is_favorite ? 'btn-danger' : 'btn-outline-light'; ?>">
|
||||
<?php echo $is_favorite ? '♥ Favorited' : '♡ 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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<?php if (!empty($restaurant['image_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($restaurant['image_url']); ?>" class="img-fluid rounded shadow-sm" alt="Image of <?php echo htmlspecialchars($restaurant['name']); ?>">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2 class="mt-5 mb-4">Menu</h2>
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<h2 class="mb-4">Menu</h2>
|
||||
<div class="menu-grid">
|
||||
<?php if ($menu_items): ?>
|
||||
<?php foreach ($menu_items as $item): ?>
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card h-100 shadow-sm border-light">
|
||||
<?php if (!empty($item['image_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($item['image_url']); ?>" class="card-img-top" alt="<?php echo htmlspecialchars($item['name']); ?>" style="height: 200px; object-fit: cover;">
|
||||
<?php endif; ?>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title"><?php echo htmlspecialchars($item['name']); ?></h5>
|
||||
<p class="card-text text-muted flex-grow-1"><?php echo htmlspecialchars($item['description']); ?></p>
|
||||
<p class="card-text h4 text-success">$<?php echo htmlspecialchars(number_format($item['price'], 2)); ?></p>
|
||||
<form action="cart_actions.php" method="post" class="mt-auto">
|
||||
<div class="menu-item-card">
|
||||
<div class="menu-item-card-content">
|
||||
<h3><?php echo htmlspecialchars($item['name']); ?></h3>
|
||||
<p class="description"><?php echo htmlspecialchars($item['description']); ?></p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="price">$<?php echo htmlspecialchars(number_format($item['price'], 2)); ?></span>
|
||||
<form action="cart_actions.php" method="post" class="add-to-cart-form">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<input type="hidden" name="restaurant_id" value="<?php echo $restaurant_id; ?>">
|
||||
<input type="hidden" name="menu_item_id" value="<?php echo $item['id']; ?>">
|
||||
<div class="input-group">
|
||||
<input type="number" name="quantity" class="form-control" value="1" min="1">
|
||||
<button type="submit" class="btn btn-primary">Add to Cart</button>
|
||||
<input type="number" name="quantity" class="form-control quantity-input" value="1" min="1">
|
||||
<button type="submit" class="btn btn-primary add-to-cart-btn">Add</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (!empty($item['image_url'])): ?>
|
||||
<img src="<?php echo htmlspecialchars($item['image_url']); ?>" class="menu-item-image" alt="<?php echo htmlspecialchars($item['name']); ?>">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<div class="col">
|
||||
<p class="alert alert-info">This restaurant has no menu items yet.</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<hr class="my-5">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mx-auto">
|
||||
<h2 class="mb-4">Reviews & Ratings</h2>
|
||||
|
||||
<?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 see reviews or leave your own.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="reviews-section">
|
||||
<h2 class="mb-4">Reviews</h2>
|
||||
<?php if ($ratings): ?>
|
||||
<?php foreach ($ratings as $rating): ?>
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<h5 class="card-title"><?php echo htmlspecialchars($rating['user_name']); ?></h5>
|
||||
<?php foreach (array_slice($ratings, 0, 3) as $rating): ?>
|
||||
<div class="review-card">
|
||||
<div class="review-header">
|
||||
<strong><?php echo htmlspecialchars($rating['user_name']); ?></strong>
|
||||
<span class="text-warning"><?php echo str_repeat('★', $rating['rating']) . str_repeat('☆', 5 - $rating['rating']); ?></span>
|
||||
</div>
|
||||
<p class="card-text"><?php echo nl2br(htmlspecialchars($rating['review'])); ?></p>
|
||||
<p class="card-text"><small class="text-muted"><?php echo date('F j, Y, g:i a', strtotime($rating['created_at'])); ?></small></p>
|
||||
</div>
|
||||
<p class="review-text">"<?php echo nl2br(htmlspecialchars($rating['review'])); ?>"</p>
|
||||
<small class="text-muted"><?php echo date('F Y', strtotime($rating['created_at'])); ?></small>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p>This restaurant has no reviews yet. Be the first!</p>
|
||||
<p>This restaurant has no reviews yet.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION['user_id'])): ?>
|
||||
<a href="leave_review.php?restaurant_id=<?php echo $restaurant_id; ?>" class="btn btn-outline-primary mt-3">Leave a Review</a>
|
||||
<?php else: ?>
|
||||
<p class="alert alert-warning">Restaurant not found.</p>
|
||||
<p class="mt-3"><a href="login.php?redirect_url=<?php echo urlencode($_SERVER['REQUEST_URI']); ?>">Log in</a> to leave a review.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require_once 'footer.php'; ?>
|
||||
15
migrations/00000000_drop_all_tables.sql
Normal file
15
migrations/00000000_drop_all_tables.sql
Normal file
@ -0,0 +1,15 @@
|
||||
DROP TABLE IF EXISTS cart CASCADE;
|
||||
DROP TABLE IF EXISTS ratings CASCADE;
|
||||
DROP TABLE IF EXISTS favorite_restaurants CASCADE;
|
||||
DROP TABLE IF EXISTS driver_assignments CASCADE;
|
||||
DROP TABLE IF EXISTS order_items CASCADE;
|
||||
DROP TABLE IF EXISTS orders CASCADE;
|
||||
DROP TABLE IF EXISTS menu_items CASCADE;
|
||||
DROP TABLE IF EXISTS special_promotions CASCADE;
|
||||
DROP TABLE IF EXISTS coupons CASCADE;
|
||||
DROP TABLE IF EXISTS restaurants CASCADE;
|
||||
DROP TABLE IF EXISTS cuisines CASCADE;
|
||||
DROP TABLE IF EXISTS drivers CASCADE;
|
||||
DROP TABLE IF EXISTS password_resets CASCADE;
|
||||
DROP TABLE IF EXISTS settings CASCADE;
|
||||
DROP TABLE IF EXISTS users CASCADE;
|
||||
1
migrations/00000000_drop_users_table.sql
Normal file
1
migrations/00000000_drop_users_table.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS users;
|
||||
7
migrations/20251011_create_cart_table.sql
Normal file
7
migrations/20251011_create_cart_table.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS cart (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT,
|
||||
menu_item_id INT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
8
migrations/20251011_create_ratings_table.sql
Normal file
8
migrations/20251011_create_ratings_table.sql
Normal file
@ -0,0 +1,8 @@
|
||||
CREATE TABLE IF NOT EXISTS ratings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
order_id INT NOT NULL,
|
||||
rating INT NOT NULL,
|
||||
comment TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
7
migrations/20251013_create_users_table.sql
Normal file
7
migrations/20251013_create_users_table.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
@ -1,18 +1,17 @@
|
||||
CREATE TABLE IF NOT EXISTS `orders` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`user_id` INT NOT NULL,
|
||||
`restaurant_id` INT NOT NULL,
|
||||
`total_price` DECIMAL(10, 2) NOT NULL,
|
||||
`status` VARCHAR(50) NOT NULL DEFAULT 'pending',
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`)
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
restaurant_id INT NOT NULL,
|
||||
total_price DECIMAL(10, 2) NOT NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `order_items` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`order_id` INT NOT NULL,
|
||||
`menu_item_id` INT NOT NULL,
|
||||
`quantity` INT NOT NULL,
|
||||
`price` DECIMAL(10, 2) NOT NULL,
|
||||
FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`)
|
||||
CREATE TABLE IF NOT EXISTS order_items (
|
||||
id SERIAL PRIMARY KEY,
|
||||
order_id INT NOT NULL,
|
||||
menu_item_id INT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
price DECIMAL(10, 2) NOT NULL,
|
||||
FOREIGN KEY (order_id) REFERENCES orders(id)
|
||||
);
|
||||
|
||||
@ -1 +0,0 @@
|
||||
ALTER TABLE ratings ADD COLUMN order_id INT;
|
||||
@ -1 +0,0 @@
|
||||
ALTER TABLE users ADD COLUMN password VARCHAR(255);
|
||||
@ -1 +0,0 @@
|
||||
ALTER TABLE orders ADD COLUMN status VARCHAR(50) NOT NULL DEFAULT 'Pending';
|
||||
1
migrations/20251015_add_token_to_orders.sql
Normal file
1
migrations/20251015_add_token_to_orders.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE orders ADD COLUMN token VARCHAR(255) UNIQUE;
|
||||
2
migrations/20251015_allow_guest_orders.sql
Normal file
2
migrations/20251015_allow_guest_orders.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE "orders" ALTER COLUMN "user_id" DROP NOT NULL;
|
||||
ALTER TABLE "orders" ADD COLUMN "guest_name" VARCHAR(255) NULL, ADD COLUMN "guest_email" VARCHAR(255) NULL;
|
||||
@ -1 +1 @@
|
||||
INSERT INTO users (email, role, password, is_admin) VALUES ('admin@example.com', 'owner', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', TRUE) ON CONFLICT (email) DO UPDATE SET role = 'owner', password = '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', is_admin = TRUE;
|
||||
INSERT INTO users (name, email, role, password, is_admin) VALUES ('Admin User', 'admin@example.com', 'owner', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', TRUE) ON CONFLICT (email) DO UPDATE SET name = 'Admin User', role = 'owner', password = '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', is_admin = TRUE;
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`name` VARCHAR(255) NOT NULL,
|
||||
`email` VARCHAR(255) NOT NULL UNIQUE,
|
||||
`password` VARCHAR(255) NOT NULL,
|
||||
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
@ -1,2 +0,0 @@
|
||||
ALTER TABLE `cart` ADD `session_id` VARCHAR(255) NULL AFTER `user_id`;
|
||||
ALTER TABLE `cart` MODIFY `user_id` INT NULL;
|
||||
2
migrations/20251016_update_cart_table.sql
Normal file
2
migrations/20251016_update_cart_table.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE cart ADD COLUMN session_id VARCHAR(255) NULL;
|
||||
ALTER TABLE cart ALTER COLUMN user_id DROP NOT NULL;
|
||||
@ -2,31 +2,128 @@
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
exit();
|
||||
}
|
||||
$order_id = $_SESSION['order_id'] ?? $_GET['order_id'] ?? null;
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$guest_token = $_SESSION['token'] ?? $_GET['token'] ?? null;
|
||||
|
||||
if (!isset($_GET['id'])) {
|
||||
if (!$order_id) {
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$orderId = $_GET['id'];
|
||||
$pdo = db();
|
||||
$order = null;
|
||||
|
||||
if ($user_id) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM orders WHERE id = ? AND user_id = ?");
|
||||
$stmt->execute([$order_id, $user_id]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
} elseif ($guest_token) {
|
||||
$stmt = $pdo->prepare("SELECT * FROM orders WHERE id = ? AND token = ?");
|
||||
$stmt->execute([$order_id, $guest_token]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
if (!$order) {
|
||||
// If the order is not found or doesn't belong to the user/guest, redirect.
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch order items
|
||||
$itemsStmt = $pdo->prepare("
|
||||
SELECT oi.quantity, mi.name, mi.price
|
||||
FROM order_items oi
|
||||
JOIN menu_items mi ON oi.menu_item_id = mi.id
|
||||
WHERE oi.order_id = :order_id
|
||||
");
|
||||
$itemsStmt->bindParam(':order_id', $order_id);
|
||||
$itemsStmt->execute();
|
||||
$orderItems = $itemsStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Determine the correct tracking URL
|
||||
$tracking_url = "order_status.php?order_id=" . $order_id;
|
||||
if (!$user_id && $guest_token) {
|
||||
$tracking_url .= "&token=" . $guest_token;
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="container mt-5 mb-5">
|
||||
<div class="row d-flex justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white text-center">
|
||||
<h2 class="mb-0">Thank You for Your Order!</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center mb-4">
|
||||
<p class="lead">Your order has been placed successfully.</p>
|
||||
<p>Your Order ID is: <strong><?php echo htmlspecialchars($order['id']); ?></strong></p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2 text-center">
|
||||
<h2 class="mb-4">Thank You for Your Order!</h2>
|
||||
<p>Your order has been placed successfully.</p>
|
||||
<p>Your Order ID is: <strong><?php echo $orderId; ?></strong></p>
|
||||
<p>We have received your order and will begin processing it shortly.</p>
|
||||
<a href="index.php" class="btn btn-primary">Continue Shopping</a>
|
||||
<div class="col-md-6">
|
||||
<h5>Delivery Details</h5>
|
||||
<p>
|
||||
<strong>Name:</strong> <?php echo htmlspecialchars($order['guest_name'] ?? 'N/A'); ?><br>
|
||||
<strong>Address:</strong> <?php echo htmlspecialchars($order['delivery_address']); ?><br>
|
||||
<strong>Phone:</strong> <?php echo htmlspecialchars($order['phone_number']); ?>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<h5>Order Summary</h5>
|
||||
<p>
|
||||
<strong>Date:</strong> <?php echo date("F j, Y, g:i a", strtotime($order['created_at'])); ?><br>
|
||||
<strong>Status:</strong> <span class="badge bg-warning text-dark"><?php echo htmlspecialchars(ucfirst($order['status'])); ?></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4">Items Ordered</h5>
|
||||
<ul class="list-group mb-3">
|
||||
<?php foreach ($orderItems as $item): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?php echo htmlspecialchars($item['name']); ?> (x<?php echo $item['quantity']; ?>)
|
||||
<span>$<?php echo number_format($item['price'] * $item['quantity'], 2); ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
<ul class="list-group mb-4">
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span>Subtotal</span>
|
||||
<span>$<?php echo number_format($order['total_price'] - ($order['discount_amount'] ?? 0), 2); ?></span>
|
||||
</li>
|
||||
<?php if (isset($order['discount_amount']) && $order['discount_amount'] > 0): ?>
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span>Discount</span>
|
||||
<span class="text-success">-$<?php echo number_format($order['discount_amount'], 2); ?></span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li class="list-group-item d-flex justify-content-between fw-bold">
|
||||
<span>Total</span>
|
||||
<span>$<?php echo number_format($order['total_price'], 2); ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="text-center order-confirmation-actions">
|
||||
<p>We've received your order and will begin processing it shortly. You can track the progress of your order using the button below.</p>
|
||||
<a href="index.php" class="btn btn-secondary">Continue Shopping</a>
|
||||
<a href="<?php echo $tracking_url; ?>" class="btn btn-primary">Track Order</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php
|
||||
include 'footer.php';
|
||||
// Clear session variables for the next order
|
||||
unset($_SESSION['order_id']);
|
||||
if(isset($_SESSION['token'])){
|
||||
unset($_SESSION['token']);
|
||||
}
|
||||
?>
|
||||
|
||||
175
order_status.php
175
order_status.php
@ -1,59 +1,68 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once 'db/config.php';
|
||||
include 'header.php';
|
||||
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: login.php");
|
||||
// Allow guest access with a token
|
||||
$order_id = $_GET['order_id'] ?? null;
|
||||
$token = $_GET['token'] ?? null;
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
|
||||
if (!$order_id) {
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_GET['order_id'])) {
|
||||
echo "<div class='container mt-5'><p>No order specified.</p></div>";
|
||||
include 'footer.php';
|
||||
exit();
|
||||
}
|
||||
|
||||
$order_id = $_GET['order_id'];
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$pdo = db();
|
||||
$order = null;
|
||||
|
||||
// Fetch order details to ensure the user owns this order
|
||||
$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);
|
||||
if ($user_id) {
|
||||
// User is logged in, verify order belongs to them
|
||||
$stmt = $pdo->prepare("SELECT o.*, r.name as restaurant_name FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND o.user_id = ?");
|
||||
$stmt->execute([$order_id, $user_id]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
} elseif ($token) {
|
||||
// Guest access, verify token
|
||||
$stmt = $pdo->prepare("SELECT o.*, r.name as restaurant_name FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND o.guest_token = ?");
|
||||
$stmt->execute([$order_id, $token]);
|
||||
$order = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
if (!$order) {
|
||||
echo "<div class='container mt-5'><p>Order not found or you do not have permission to view it.</p></div>";
|
||||
include 'header.php';
|
||||
echo "<div class='container mt-5'><div class='alert alert-danger'>Order not found or you do not have permission to view it.</div></div>";
|
||||
include 'footer.php';
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch order items
|
||||
$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);
|
||||
$stmt = $pdo->prepare("SELECT oi.*, mi.name as item_name FROM order_items oi JOIN menu_items mi ON oi.menu_item_id = mi.id WHERE oi.order_id = ?");
|
||||
$stmt->execute([$order_id]);
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container mt-5">
|
||||
<h2>Order Status for #<?php echo $order['id']; ?></h2>
|
||||
<hr>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Live Status</h5>
|
||||
<div class="row">
|
||||
<div class="col-lg-8 offset-lg-2">
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title fw-bold">Thank You For Your Order!</h1>
|
||||
<p class="text-muted">Order #<?php echo $order['id']; ?></p>
|
||||
<p><strong>Restaurant:</strong> <?php echo htmlspecialchars($order['restaurant_name']); ?></p>
|
||||
<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(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>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title text-center mb-4">Order Status</h4>
|
||||
<div id="order-status-timeline">
|
||||
<!-- Timeline will be dynamically generated by JavaScript -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card shadow-sm mt-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Order Summary</h5>
|
||||
<ul class="list-group list-group-flush">
|
||||
@ -63,60 +72,102 @@ $items = $p_items->fetchAll(PDO::FETCH_ASSOC);
|
||||
<span>$<?php echo number_format($item['price'] * $item['quantity'], 2); ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<strong>Total</strong>
|
||||
<strong>$<?php echo number_format($order['total_price'], 2); ?></strong>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center fw-bold">
|
||||
Total
|
||||
<span>$<?php echo number_format($order['total_price'], 2); ?></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<a href="order_history.php" class="btn btn-secondary mt-3">Back to Order History</a>
|
||||
<div class="text-center mt-4">
|
||||
<a href="index.php" class="btn btn-primary">Back to Home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const orderId = <?php echo $order_id; ?>;
|
||||
const statusElement = document.getElementById('order-status');
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
const token = '<?php echo $token; ?>';
|
||||
const timelineContainer = document.getElementById('order-status-timeline');
|
||||
const currentStatus = '<?php echo $order['status']; ?>';
|
||||
|
||||
const statusToProgress = {
|
||||
'Pending': 10,
|
||||
'Preparing': 40,
|
||||
'Out For Delivery': 75,
|
||||
'Delivered': 100,
|
||||
'Cancelled': 0
|
||||
};
|
||||
const statuses = [
|
||||
{ name: 'Pending', desc: 'Your order has been placed and is waiting for the restaurant to accept it.' },
|
||||
{ name: 'Preparing', desc: 'The restaurant is preparing your food.' },
|
||||
{ name: 'Out For Delivery', desc: 'A driver is on their way to you.' },
|
||||
{ name: 'Delivered', desc: 'Your order has been delivered. Enjoy!' }
|
||||
];
|
||||
|
||||
function updateProgress(status) {
|
||||
const progress = statusToProgress[status] || 0;
|
||||
progressBar.style.width = progress + '%';
|
||||
progressBar.textContent = status;
|
||||
const cancelledStatus = { name: 'Cancelled', desc: 'This order has been cancelled.' };
|
||||
|
||||
if (status === 'Delivered') {
|
||||
progressBar.classList.remove('progress-bar-animated', 'bg-primary');
|
||||
progressBar.classList.add('bg-success');
|
||||
} else if (status === 'Cancelled') {
|
||||
progressBar.classList.remove('progress-bar-animated', 'bg-primary');
|
||||
progressBar.classList.add('bg-danger');
|
||||
} else {
|
||||
progressBar.classList.remove('bg-success', 'bg-danger');
|
||||
progressBar.classList.add('bg-primary', 'progress-bar-animated');
|
||||
function renderTimeline(status) {
|
||||
timelineContainer.innerHTML = '';
|
||||
let activeIndex = statuses.findIndex(s => s.name.toLowerCase() === status.toLowerCase());
|
||||
|
||||
if (status.toLowerCase() === 'cancelled') {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'timeline-item timeline-cancelled';
|
||||
item.innerHTML = `
|
||||
<div class="timeline-icon"><i class="fas fa-times-circle"></i></div>
|
||||
<div class="timeline-content">
|
||||
<h5 class="fw-bold">${cancelledStatus.name}</h5>
|
||||
<p class="text-muted">${cancelledStatus.desc}</p>
|
||||
</div>
|
||||
`;
|
||||
timelineContainer.appendChild(item);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
statuses.forEach((s, index) => {
|
||||
const item = document.createElement('div');
|
||||
let itemClass = 'timeline-item';
|
||||
let icon = '<i class="far fa-circle"></i>';
|
||||
|
||||
if (index < activeIndex) {
|
||||
itemClass += ' timeline-complete';
|
||||
icon = '<i class="fas fa-check-circle"></i>';
|
||||
} else if (index === activeIndex) {
|
||||
itemClass += ' timeline-active';
|
||||
icon = '<i class="fas fa-dot-circle"></i>';
|
||||
}
|
||||
|
||||
item.className = itemClass;
|
||||
item.innerHTML = `
|
||||
<div class="timeline-icon">${icon}</div>
|
||||
<div class="timeline-content">
|
||||
<h5 class="fw-bold">${s.name}</h5>
|
||||
<p class="text-muted">${s.desc}</p>
|
||||
</div>
|
||||
`;
|
||||
timelineContainer.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchStatus() {
|
||||
fetch(`api/get_order_status.php?order_id=${orderId}`)
|
||||
let url = `api/get_order_status.php?order_id=${orderId}`;
|
||||
if (token) {
|
||||
url += `&token=${token}`;
|
||||
}
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status && data.status !== statusElement.textContent) {
|
||||
statusElement.textContent = data.status;
|
||||
updateProgress(data.status);
|
||||
if (data.status) {
|
||||
renderTimeline(data.status);
|
||||
} else if(data.error) {
|
||||
console.error('Error fetching status:', data.error);
|
||||
timelineContainer.innerHTML = '<div class="alert alert-warning">Could not retrieve order status. Please try again later.</div>';
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error fetching status:', error));
|
||||
.catch(error => {
|
||||
console.error('Fetch error:', error);
|
||||
timelineContainer.innerHTML = '<div class="alert alert-danger">An error occurred while trying to update the order status.</div>';
|
||||
});
|
||||
}
|
||||
|
||||
updateProgress(statusElement.textContent);
|
||||
renderTimeline(currentStatus);
|
||||
setInterval(fetchStatus, 10000); // Poll every 10 seconds
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -11,25 +11,44 @@ if (!isset($_GET['session_id'])) {
|
||||
|
||||
$stripe_session_id = $_GET['session_id'];
|
||||
$pdo = db();
|
||||
$session_id = session_id();
|
||||
|
||||
\Stripe\Stripe::setApiKey($stripeSecretKey);
|
||||
|
||||
try {
|
||||
$checkout_session = \Stripe\Checkout\Session::retrieve($stripe_session_id);
|
||||
$metadata = $checkout_session->metadata;
|
||||
$user_id = $metadata->user_id;
|
||||
$coupon_id = $metadata->coupon_id;
|
||||
$user_id = $metadata->user_id ?? null;
|
||||
$is_guest = !$user_id;
|
||||
|
||||
if ($checkout_session->payment_status == 'paid') {
|
||||
// Fetch user's address
|
||||
$stmt = $pdo->prepare("SELECT address FROM users WHERE id = ?");
|
||||
$delivery_address = null;
|
||||
$phone_number = null;
|
||||
$guest_name = null;
|
||||
$guest_email = null;
|
||||
$guest_token = null;
|
||||
|
||||
if ($is_guest) {
|
||||
$guest_name = $metadata->guest_name ?? '';
|
||||
$guest_email = $metadata->guest_email ?? '';
|
||||
$delivery_address = $metadata->guest_address ?? 'N/A';
|
||||
$phone_number = $metadata->guest_phone ?? 'N/A';
|
||||
$cart_identifier = $session_id;
|
||||
$cart_column = 'session_id';
|
||||
$guest_token = $metadata->token ?? null; // Use token from metadata
|
||||
} else {
|
||||
$stmt = $pdo->prepare("SELECT address, phone FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch();
|
||||
$delivery_address = $user ? $user['address'] : 'N/A';
|
||||
$phone_number = $user ? $user['phone'] : 'N/A';
|
||||
$cart_identifier = $user_id;
|
||||
$cart_column = 'user_id';
|
||||
}
|
||||
|
||||
// Fetch cart items
|
||||
$stmt = $pdo->prepare("SELECT c.*, mi.price, mi.restaurant_id FROM cart c JOIN menu_items mi ON c.menu_item_id = mi.id WHERE c.user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$stmt = $pdo->prepare("SELECT c.*, mi.price, mi.restaurant_id FROM cart c JOIN menu_items mi ON c.menu_item_id = mi.id WHERE c.$cart_column = ?");
|
||||
$stmt->execute([$cart_identifier]);
|
||||
$cart_items = $stmt->fetchAll();
|
||||
|
||||
if (empty($cart_items)) {
|
||||
@ -39,11 +58,12 @@ try {
|
||||
|
||||
$total_price = $_SESSION['total_price'] ?? 0;
|
||||
$discount_amount = $_SESSION['discount_amount'] ?? 0;
|
||||
$coupon_id = $metadata->coupon_id ?? null;
|
||||
$restaurant_id = $cart_items[0]['restaurant_id']; // Assuming order from one restaurant
|
||||
|
||||
// Create order
|
||||
$stmt = $pdo->prepare("INSERT INTO orders (user_id, restaurant_id, total_price, status, stripe_session_id, delivery_address, coupon_id, discount_amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $restaurant_id, $total_price, 'paid', $stripe_session_id, $delivery_address, $coupon_id, $discount_amount]);
|
||||
$stmt = $pdo->prepare("INSERT INTO orders (user_id, restaurant_id, total_price, status, stripe_session_id, delivery_address, phone_number, coupon_id, discount_amount, guest_name, guest_email, token) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $restaurant_id, $total_price, 'paid', $stripe_session_id, $delivery_address, $phone_number, $coupon_id, $discount_amount, $guest_name, $guest_email, $guest_token]);
|
||||
$order_id = $pdo->lastInsertId();
|
||||
|
||||
// Insert order items
|
||||
@ -53,8 +73,8 @@ try {
|
||||
}
|
||||
|
||||
// Clear cart
|
||||
$stmt = $pdo->prepare("DELETE FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$stmt = $pdo->prepare("DELETE FROM cart WHERE $cart_column = ?");
|
||||
$stmt->execute([$cart_identifier]);
|
||||
|
||||
// Clear coupon session variables
|
||||
unset($_SESSION['coupon_id']);
|
||||
@ -64,8 +84,11 @@ try {
|
||||
unset($_SESSION['discount_amount']);
|
||||
unset($_SESSION['subtotal']);
|
||||
|
||||
|
||||
$_SESSION['order_id'] = $order_id;
|
||||
if ($is_guest) {
|
||||
$_SESSION['token'] = $guest_token;
|
||||
}
|
||||
|
||||
header("Location: order_confirmation.php");
|
||||
exit();
|
||||
|
||||
|
||||
@ -5,13 +5,15 @@ require_once 'includes/api_keys.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isset($_SESSION['user_id']) || !isset($_POST['orderID'])) {
|
||||
if (!isset($_POST['orderID'])) {
|
||||
echo json_encode(['error' => 'Invalid request.']);
|
||||
exit();
|
||||
}
|
||||
|
||||
$orderID = $_POST['orderID'];
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
$is_guest = !$user_id;
|
||||
$session_id = session_id();
|
||||
|
||||
// Helper function to get PayPal access token
|
||||
function get_paypal_access_token($clientId, $secret, $apiBase) {
|
||||
@ -56,15 +58,26 @@ $details = json_decode($result);
|
||||
if (isset($details->status) && $details->status == 'COMPLETED') {
|
||||
$pdo = db();
|
||||
|
||||
// Fetch user's address
|
||||
$stmt = $pdo->prepare("SELECT address FROM users WHERE id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$user = $stmt->fetch();
|
||||
$delivery_address = $user ? $user['address'] : 'N/A';
|
||||
$delivery_address = $_POST['address'] ?? 'N/A';
|
||||
$phone_number = $_POST['phone'] ?? 'N/A';
|
||||
$guest_name = null;
|
||||
$guest_email = null;
|
||||
$guest_token = null;
|
||||
|
||||
if ($is_guest) {
|
||||
$guest_name = $_POST['name'] ?? '';
|
||||
$guest_email = $_POST['email'] ?? '';
|
||||
$cart_identifier = $session_id;
|
||||
$cart_column = 'session_id';
|
||||
$guest_token = bin2hex(random_bytes(16)); // Generate a unique token for guest orders
|
||||
} else {
|
||||
$cart_identifier = $user_id;
|
||||
$cart_column = 'user_id';
|
||||
}
|
||||
|
||||
// Fetch cart items
|
||||
$stmt = $pdo->prepare("SELECT c.*, mi.price, mi.restaurant_id FROM cart c JOIN menu_items mi ON c.menu_item_id = mi.id WHERE c.user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$stmt = $pdo->prepare("SELECT c.*, mi.price, mi.restaurant_id FROM cart c JOIN menu_items mi ON c.menu_item_id = mi.id WHERE c.$cart_column = ?");
|
||||
$stmt->execute([$cart_identifier]);
|
||||
$cart_items = $stmt->fetchAll();
|
||||
|
||||
if (empty($cart_items)) {
|
||||
@ -78,8 +91,8 @@ if (isset($details->status) && $details->status == 'COMPLETED') {
|
||||
$restaurant_id = $cart_items[0]['restaurant_id']; // Assuming order from one restaurant
|
||||
|
||||
// Create order
|
||||
$stmt = $pdo->prepare("INSERT INTO orders (user_id, restaurant_id, total_price, status, stripe_session_id, delivery_address, coupon_id, discount_amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $restaurant_id, $total_price, 'paid', $orderID, $delivery_address, $coupon_id, $discount_amount]);
|
||||
$stmt = $pdo->prepare("INSERT INTO orders (user_id, restaurant_id, total_price, status, stripe_session_id, delivery_address, phone_number, coupon_id, discount_amount, guest_name, guest_email, token) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $restaurant_id, $total_price, 'paid', $orderID, $delivery_address, $phone_number, $coupon_id, $discount_amount, $guest_name, $guest_email, $guest_token]);
|
||||
$order_id = $pdo->lastInsertId();
|
||||
|
||||
// Insert order items
|
||||
@ -89,8 +102,8 @@ if (isset($details->status) && $details->status == 'COMPLETED') {
|
||||
}
|
||||
|
||||
// Clear cart
|
||||
$stmt = $pdo->prepare("DELETE FROM cart WHERE user_id = ?");
|
||||
$stmt->execute([$user_id]);
|
||||
$stmt = $pdo->prepare("DELETE FROM cart WHERE $cart_column = ?");
|
||||
$stmt->execute([$cart_identifier]);
|
||||
|
||||
// Clear coupon session variables
|
||||
unset($_SESSION['coupon_id']);
|
||||
@ -101,6 +114,10 @@ if (isset($details->status) && $details->status == 'COMPLETED') {
|
||||
unset($_SESSION['subtotal']);
|
||||
|
||||
$_SESSION['order_id'] = $order_id;
|
||||
if ($is_guest) {
|
||||
$_SESSION['token'] = $guest_token;
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'order_id' => $order_id]);
|
||||
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user