685 lines
29 KiB
PHP
685 lines
29 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
error_reporting(E_ALL);
|
|
ini_set("display_errors", "1");
|
|
require_once __DIR__ . '/db/config.php';
|
|
require_once __DIR__ . '/includes/functions.php';
|
|
|
|
$pdo = db();
|
|
$settings = get_company_settings();
|
|
|
|
// Support both 'table_id' and 'table' for backward compatibility or different implementations
|
|
$table_id = isset($_GET['table_id']) ? (int)$_GET['table_id'] : (isset($_GET['table']) ? (int)$_GET['table'] : 0);
|
|
|
|
if ($table_id <= 0) {
|
|
die("Invalid table ID ($table_id). Please scan the QR code on your table.");
|
|
}
|
|
|
|
// Fetch table and outlet info
|
|
try {
|
|
$stmt = $pdo->prepare("
|
|
SELECT t.id, t.table_number AS table_name, a.outlet_id, o.name AS outlet_name
|
|
FROM `tables` t
|
|
JOIN areas a ON t.area_id = a.id
|
|
JOIN outlets o ON a.outlet_id = o.id
|
|
WHERE t.id = ?
|
|
");
|
|
$stmt->execute([$table_id]);
|
|
$table_info = $stmt->fetch();
|
|
|
|
if (!$table_info) {
|
|
die("Table with ID $table_id not found. Please contact staff.");
|
|
}
|
|
} catch (PDOException $e) {
|
|
die("Database error: " . $e->getMessage());
|
|
}
|
|
|
|
$outlet_id = (int)$table_info['outlet_id'];
|
|
$categories = $pdo->query("SELECT * FROM categories ORDER BY sort_order")->fetchAll();
|
|
$all_products = $pdo->query("SELECT p.*, c.name as category_name, c.name_ar as category_name_ar FROM products p JOIN categories c ON p.category_id = c.id WHERE p.deleted_at IS NULL")->fetchAll();
|
|
|
|
// Fetch variants
|
|
$variants_raw = $pdo->query("SELECT * FROM product_variants WHERE deleted_at IS NULL ORDER BY price_adjustment ASC")->fetchAll();
|
|
$variants_by_product = [];
|
|
foreach ($variants_raw as $v) {
|
|
$variants_by_product[$v['product_id']][] = $v;
|
|
}
|
|
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
|
<title><?= htmlspecialchars($settings['company_name']) ?> - Order Online</title>
|
|
<?php if (!empty($settings['favicon_url'])): ?>
|
|
<link rel="icon" href="<?= get_base_url() . htmlspecialchars($settings['favicon_url']) ?>?v=<?= time() ?>">
|
|
<?php endif; ?>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=Noto+Sans+Arabic:wght@400;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--primary-color: #2563eb;
|
|
--secondary-color: #64748b;
|
|
--bg-light: #f8fafc;
|
|
--card-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
--primary-font: 'Plus Jakarta Sans', sans-serif;
|
|
--arabic-font: 'Noto Sans Arabic', sans-serif;
|
|
}
|
|
body {
|
|
font-family: var(--primary-font);
|
|
background-color: var(--bg-light);
|
|
color: #1e293b;
|
|
padding-bottom: 100px;
|
|
}
|
|
body.lang-ar { font-family: var(--arabic-font); direction: rtl; text-align: right; }
|
|
|
|
/* Header & Hero */
|
|
.hero-section {
|
|
background: white;
|
|
padding: 2rem 0;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.company-logo {
|
|
height: 48px;
|
|
width: auto;
|
|
object-fit: contain;
|
|
}
|
|
|
|
/* Category Navigation */
|
|
.category-nav-wrapper {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1020;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
backdrop-filter: blur(8px);
|
|
border-bottom: 1px solid #e2e8f0;
|
|
padding: 0.75rem 0;
|
|
}
|
|
.category-nav {
|
|
overflow-x: auto;
|
|
white-space: nowrap;
|
|
-webkit-overflow-scrolling: touch;
|
|
padding: 0 1rem;
|
|
}
|
|
.category-nav::-webkit-scrollbar { display: none; }
|
|
.category-item {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.6rem 1.25rem;
|
|
border-radius: 9999px;
|
|
background: white;
|
|
margin-right: 0.5rem;
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
cursor: pointer;
|
|
border: 1px solid #e2e8f0;
|
|
transition: all 0.2s ease;
|
|
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
}
|
|
.lang-ar .category-item { margin-right: 0; margin-left: 0.5rem; }
|
|
.category-item:hover { border-color: var(--primary-color); color: var(--primary-color); }
|
|
.category-item.active {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 4px 6px -1px rgba(37, 99, 235, 0.2);
|
|
}
|
|
|
|
/* Product Grid - 5 columns on desktop */
|
|
.product-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 1rem;
|
|
}
|
|
@media (min-width: 576px) { .product-grid { grid-template-columns: repeat(3, 1fr); } }
|
|
@media (min-width: 992px) { .product-grid { grid-template-columns: repeat(4, 1fr); } }
|
|
@media (min-width: 1200px) { .product-grid { grid-template-columns: repeat(5, 1fr); } }
|
|
|
|
.product-card {
|
|
background: white;
|
|
border: 1px solid #f1f5f9;
|
|
border-radius: 1rem;
|
|
overflow: hidden;
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
position: relative;
|
|
cursor: pointer;
|
|
height: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.product-card:hover {
|
|
transform: translateY(-4px);
|
|
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
|
|
border-color: #cbd5e1;
|
|
}
|
|
.product-card:active { transform: scale(0.97); }
|
|
|
|
.product-img-container {
|
|
position: relative;
|
|
width: 100%;
|
|
padding-top: 100%; /* 1:1 Aspect Ratio */
|
|
background: #f1f5f9;
|
|
overflow: hidden;
|
|
}
|
|
.product-img {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.product-info { padding: 0.75rem; flex-grow: 1; display: flex; flex-direction: column; }
|
|
.product-title {
|
|
font-weight: 700;
|
|
font-size: 0.95rem;
|
|
margin-bottom: 0.25rem;
|
|
color: #0f172a;
|
|
line-height: 1.2;
|
|
}
|
|
.product-price { font-weight: 800; color: var(--primary-color); font-size: 1rem; margin-top: auto; }
|
|
|
|
.sale-badge {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
left: 0.5rem;
|
|
background: #ef4444;
|
|
color: white;
|
|
font-size: 0.7rem;
|
|
font-weight: 800;
|
|
padding: 0.2rem 0.6rem;
|
|
border-radius: 9999px;
|
|
z-index: 10;
|
|
}
|
|
.lang-ar .sale-badge { left: auto; right: 0.5rem; }
|
|
|
|
/* Cart Footer */
|
|
.cart-footer {
|
|
position: fixed;
|
|
bottom: 1.5rem;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 90%;
|
|
max-width: 500px;
|
|
background: #1e293b;
|
|
padding: 0.75rem 1.25rem;
|
|
border-radius: 1.25rem;
|
|
z-index: 1030;
|
|
display: none;
|
|
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.2);
|
|
color: white;
|
|
}
|
|
|
|
/* Footer */
|
|
.site-footer {
|
|
padding: 3rem 0;
|
|
background: #f1f5f9;
|
|
border-top: 1px solid #e2e8f0;
|
|
margin-top: 3rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.lang-toggle {
|
|
font-size: 0.85rem;
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.75rem;
|
|
border: 1px solid #e2e8f0;
|
|
background: white;
|
|
transition: all 0.2s;
|
|
}
|
|
.lang-toggle:hover { background: #f1f5f9; }
|
|
|
|
.name-en { display: block; }
|
|
.name-ar { display: none; }
|
|
.lang-ar .name-en { display: none; }
|
|
.lang-ar .name-ar { display: block; }
|
|
|
|
.both-names .name-en { display: block; }
|
|
.both-names .name-ar { display: block; font-size: 0.8em; opacity: 0.8; margin-top: 2px; }
|
|
|
|
.cart-item-img { width: 60px; height: 60px; object-fit: cover; border-radius: 0.75rem; }
|
|
.quantity-btn {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 0.75rem;
|
|
border: 1px solid #e2e8f0;
|
|
background: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: all 0.2s;
|
|
}
|
|
.quantity-btn:active { background: #f1f5f9; transform: scale(0.9); }
|
|
</style>
|
|
</head>
|
|
<body class="both-names">
|
|
|
|
<!-- Hero Section -->
|
|
<div class="hero-section">
|
|
<div class="container text-center">
|
|
<div class="d-flex flex-column align-items-center mb-3">
|
|
<?php if (!empty($settings['logo_url'])): ?>
|
|
<img src="<?= get_base_url() . htmlspecialchars($settings['logo_url']) ?>?v=<?= time() ?>" alt="Logo" class="company-logo mb-3">
|
|
<?php endif; ?>
|
|
<h1 class="h4 fw-bold mb-1"><?= htmlspecialchars($settings['company_name']) ?></h1>
|
|
<p class="text-muted small mb-0">
|
|
<i class="bi bi-geo-alt-fill me-1"></i> <?= htmlspecialchars($table_info['outlet_name']) ?> •
|
|
<span class="badge bg-primary-subtle text-primary border border-primary-subtle">
|
|
<span data-t="table">Table</span> <?= htmlspecialchars((string)$table_info['table_name']) ?>
|
|
</span>
|
|
</p>
|
|
</div>
|
|
<div class="d-flex justify-content-center gap-2 mt-3">
|
|
<div class="lang-toggle" onclick="toggleLanguage()" id="lang-btn">AR</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Category Nav -->
|
|
<div class="category-nav-wrapper">
|
|
<div class="category-nav">
|
|
<div class="category-item active" onclick="filterCategory('all', this)">
|
|
<i class="bi bi-grid-fill"></i>
|
|
<div>
|
|
<span class="name-en">All</span>
|
|
<span class="name-ar">الكل</span>
|
|
</div>
|
|
</div>
|
|
<?php foreach ($categories as $cat): ?>
|
|
<div class="category-item" onclick="filterCategory(<?= $cat['id'] ?>, this)">
|
|
<?php if (!empty($cat['image_url'])): ?>
|
|
<img src="<?= htmlspecialchars(strpos($cat['image_url'], 'http') === 0 ? $cat['image_url'] : get_base_url() . $cat['image_url']) ?>" alt="" style="width: 20px; height: 20px; object-fit: cover; border-radius: 4px;">
|
|
<?php else: ?>
|
|
<i class="bi bi-tag-fill"></i>
|
|
<?php endif; ?>
|
|
<div>
|
|
<span class="name-en"><?= htmlspecialchars($cat['name']) ?></span>
|
|
<span class="name-ar"><?= htmlspecialchars($cat['name_ar'] ?: $cat['name']) ?></span>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="container mt-4">
|
|
<div class="product-grid" id="products-container">
|
|
<?php foreach ($all_products as $product):
|
|
$has_variants = !empty($variants_by_product[$product['id']]);
|
|
$effective_price = get_product_price($product);
|
|
$is_promo = $effective_price < (float)$product['price'];
|
|
?>
|
|
<div class="product-item" data-category-id="<?= $product['category_id'] ?>">
|
|
<div class="product-card"
|
|
onclick="handleProductClick(<?= htmlspecialchars((string)json_encode([
|
|
'id' => $product['id'],
|
|
'name' => $product['name'],
|
|
'name_ar' => $product['name_ar'] ?: $product['name'],
|
|
'price' => (float)$effective_price,
|
|
'has_variants' => $has_variants,
|
|
'image' => !empty($product['image_url']) ? (strpos($product['image_url'], 'http') === 0 ? $product['image_url'] : get_base_url() . $product['image_url']) : null
|
|
])) ?>)">
|
|
|
|
<div class="product-img-container">
|
|
<?php if ($is_promo): ?>
|
|
<span class="sale-badge">SALE</span>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!empty($product['image_url'])): ?>
|
|
<img src="<?= htmlspecialchars(strpos($product['image_url'], 'http') === 0 ? $product['image_url'] : get_base_url() . $product['image_url']) ?>?v=<?= time() ?>" class="product-img" alt="<?= htmlspecialchars($product['name']) ?>" loading="lazy">
|
|
<?php else: ?>
|
|
<div class="product-img d-flex align-items-center justify-content-center bg-light text-muted opacity-25">
|
|
<i class="bi bi-image fs-1"></i>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="product-info">
|
|
<h3 class="product-title">
|
|
<span class="name-en"><?= htmlspecialchars($product['name']) ?></span>
|
|
<span class="name-ar" dir="rtl"><?= htmlspecialchars($product['name_ar'] ?: $product['name']) ?></span>
|
|
</h3>
|
|
<div class="product-price">
|
|
<?php if ($is_promo): ?>
|
|
<span class="text-danger"><?= format_currency($effective_price) ?></span>
|
|
<small class="text-muted text-decoration-line-through ms-1 opacity-50" style="font-size: 0.75rem;"><?= format_currency((float)$product['price']) ?></small>
|
|
<?php else: ?>
|
|
<?= format_currency((float)$product['price']) ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Site Footer -->
|
|
<footer class="site-footer">
|
|
<div class="container">
|
|
<p class="mb-2 fw-bold"><?= htmlspecialchars($settings['company_name']) ?></p>
|
|
<p class="text-muted small mb-3"><?= htmlspecialchars($settings['address'] ?? '') ?></p>
|
|
<div class="d-flex justify-content-center gap-3 mb-4">
|
|
<a href="#" class="text-secondary"><i class="bi bi-facebook fs-5"></i></a>
|
|
<a href="#" class="text-secondary"><i class="bi bi-instagram fs-5"></i></a>
|
|
<a href="#" class="text-secondary"><i class="bi bi-whatsapp fs-5"></i></a>
|
|
</div>
|
|
<hr class="w-25 mx-auto mb-4">
|
|
<p class="text-muted small">© <?= date('Y') ?> All Rights Reserved. Powered by Gemini POS</p>
|
|
</div>
|
|
</footer>
|
|
|
|
<!-- Cart Bar -->
|
|
<div class="cart-footer" id="cart-footer">
|
|
<div class="d-flex align-items-center justify-content-between" onclick="showCart()" style="cursor: pointer;">
|
|
<div class="d-flex align-items-center gap-3">
|
|
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
|
|
<i class="bi bi-bag-check-fill"></i>
|
|
</div>
|
|
<div>
|
|
<div class="small opacity-75"><span id="cart-items-count">0</span> <span data-t="items">Items</span></div>
|
|
<div class="fw-bold fs-5" id="cart-total-display"><?= format_currency(0) ?></div>
|
|
</div>
|
|
</div>
|
|
<div class="fw-bold">
|
|
<span data-t="view_cart">View Cart</span> <i class="bi bi-chevron-right ms-1"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cart Modal -->
|
|
<div class="modal fade" id="cartModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-fullscreen-sm-down">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<div class="modal-header border-0 pb-0">
|
|
<h5 class="modal-title fw-bold" data-t="your_order">Your Order</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="cart-list" class="mb-4">
|
|
<!-- Cart items -->
|
|
</div>
|
|
|
|
<div class="bg-light p-3 rounded-4 mb-4">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span class="text-muted" data-t="subtotal">Subtotal</span>
|
|
<span id="modal-subtotal" class="fw-bold"><?= format_currency(0) ?></span>
|
|
</div>
|
|
<div class="d-flex justify-content-between border-top pt-2 mt-2">
|
|
<span class="fw-bold fs-5" data-t="total">Total</span>
|
|
<span id="modal-total" class="fw-bold fs-5 text-primary"><?= format_currency(0) ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label class="form-label small fw-bold text-secondary" data-t="cust_name_label">Your Name (Optional)</label>
|
|
<input type="text" id="cust-name" class="form-control form-control-lg border-0 bg-light rounded-3" data-t-placeholder="cust_name_placeholder" placeholder="To identify your order">
|
|
</div>
|
|
|
|
<button class="btn btn-primary btn-lg w-100 py-3 rounded-4 fw-bold shadow-sm" id="btn-place-order" onclick="placeOrder()">
|
|
<span data-t="place_order">Place Order</span> <i class="bi bi-arrow-right-short ms-1"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Variant Modal -->
|
|
<div class="modal fade" id="variantModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<div class="modal-header border-0">
|
|
<h5 class="modal-title fw-bold" id="variantTitle" data-t="select_option">Select Option</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body p-0">
|
|
<div id="variant-list" class="list-group list-group-flush">
|
|
<!-- Variants -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
<script>
|
|
const TABLE_ID = <?= $table_id ?>;
|
|
const OUTLET_ID = <?= $outlet_id ?>;
|
|
const PRODUCT_VARIANTS = <?= json_encode($variants_by_product) ?>;
|
|
const COMPANY_SETTINGS = <?= json_encode($settings) ?>;
|
|
|
|
let cart = [];
|
|
let currentLang = localStorage.getItem('qorder_lang') || 'en';
|
|
const cartModal = new bootstrap.Modal(document.getElementById('cartModal'));
|
|
const variantModal = new bootstrap.Modal(document.getElementById('variantModal'));
|
|
|
|
const translations = {
|
|
en: {
|
|
table: 'Table',
|
|
items: 'Items',
|
|
view_cart: 'View Cart',
|
|
your_order: 'Your Order',
|
|
subtotal: 'Subtotal',
|
|
total: 'Total',
|
|
cust_name_label: 'Your Name (Optional)',
|
|
cust_name_placeholder: 'To identify your order',
|
|
place_order: 'Place Order',
|
|
select_option: 'Select Option',
|
|
added_to_cart: 'added to cart',
|
|
order_placed: 'Order Placed!',
|
|
order_success_msg: 'Your order has been sent to the kitchen. Thank you!',
|
|
placing_order: 'Placing Order...'
|
|
},
|
|
ar: {
|
|
table: 'طاولة',
|
|
items: 'أصناف',
|
|
view_cart: 'عرض السلة',
|
|
your_order: 'طلبك',
|
|
subtotal: 'المجموع الفرعي',
|
|
total: 'الإجمالي',
|
|
cust_name_label: 'اسمك (اختياري)',
|
|
cust_name_placeholder: 'لتحديد طلبك',
|
|
place_order: 'إتمام الطلب',
|
|
select_option: 'اختر الخيار',
|
|
added_to_cart: 'تم الإضافة إلى السلة',
|
|
order_placed: 'تم إرسال الطلب!',
|
|
order_success_msg: 'تم إرسال طلبك إلى المطبخ. شكراً لك!',
|
|
placing_order: 'جاري إرسال الطلب...'
|
|
}
|
|
};
|
|
|
|
function formatCurrency(amount) {
|
|
const symbol = COMPANY_SETTINGS.currency_symbol || '$';
|
|
const decimals = parseInt(COMPANY_SETTINGS.currency_decimals || 2);
|
|
const position = COMPANY_SETTINGS.currency_position || 'before';
|
|
const formatted = parseFloat(Math.abs(amount)).toFixed(decimals);
|
|
return position === 'after' ? formatted + ' ' + symbol : symbol + formatted;
|
|
}
|
|
|
|
function updateTranslations() {
|
|
document.querySelectorAll('[data-t]').forEach(el => {
|
|
const key = el.getAttribute('data-t');
|
|
if (translations[currentLang][key]) el.textContent = translations[currentLang][key];
|
|
});
|
|
document.querySelectorAll('[data-t-placeholder]').forEach(el => {
|
|
const key = el.getAttribute('data-t-placeholder');
|
|
if (translations[currentLang][key]) el.placeholder = translations[currentLang][key];
|
|
});
|
|
document.getElementById('lang-btn').textContent = currentLang === 'en' ? 'AR' : 'EN';
|
|
document.body.classList.toggle('lang-ar', currentLang === 'ar');
|
|
updateCartUI();
|
|
}
|
|
|
|
function toggleLanguage() {
|
|
currentLang = currentLang === 'en' ? 'ar' : 'en';
|
|
localStorage.setItem('qorder_lang', currentLang);
|
|
updateTranslations();
|
|
}
|
|
|
|
updateTranslations();
|
|
|
|
function filterCategory(catId, el) {
|
|
document.querySelectorAll('.category-item').forEach(i => i.classList.remove('active'));
|
|
el.classList.add('active');
|
|
document.querySelectorAll('.product-item').forEach(item => {
|
|
item.style.display = (catId === 'all' || item.dataset.categoryId == catId) ? 'block' : 'none';
|
|
});
|
|
}
|
|
|
|
function handleProductClick(product) {
|
|
if (product.has_variants) {
|
|
showVariants(product);
|
|
} else {
|
|
addToCart(product.id, currentLang === 'ar' ? product.name_ar : product.name, product.price, null, product.image);
|
|
}
|
|
}
|
|
|
|
function showVariants(product) {
|
|
const variants = PRODUCT_VARIANTS[product.id] || [];
|
|
const container = document.getElementById('variant-list');
|
|
document.getElementById('variantTitle').textContent = currentLang === 'ar' ? product.name_ar : product.name;
|
|
container.innerHTML = '';
|
|
|
|
variants.forEach(v => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'list-group-item list-group-item-action d-flex justify-content-between align-items-center py-3 border-0';
|
|
const adj = parseFloat(v.price_adjustment);
|
|
const finalPrice = product.price + adj;
|
|
const vName = currentLang === 'ar' && v.name_ar ? v.name_ar : v.name;
|
|
const pName = currentLang === 'ar' ? product.name_ar : product.name;
|
|
|
|
btn.innerHTML = `
|
|
<div>
|
|
<div class="fw-bold">${vName}</div>
|
|
<div class="small text-muted">${adj > 0 ? '+' : ''}${formatCurrency(adj)}</div>
|
|
</div>
|
|
<div class="fw-bold text-primary">${formatCurrency(finalPrice)}</div>
|
|
`;
|
|
btn.onclick = () => {
|
|
addToCart(product.id, `${pName} (${vName})`, finalPrice, v.id, product.image);
|
|
variantModal.hide();
|
|
};
|
|
container.appendChild(btn);
|
|
});
|
|
variantModal.show();
|
|
}
|
|
|
|
function addToCart(pid, name, price, vid = null, image = null) {
|
|
const existing = cart.find(i => i.product_id === pid && i.variant_id === vid);
|
|
if (existing) {
|
|
existing.quantity++;
|
|
} else {
|
|
cart.push({ product_id: pid, name, unit_price: price, variant_id: vid, quantity: 1, image });
|
|
}
|
|
updateCartUI();
|
|
showToast(name + ' ' + translations[currentLang].added_to_cart);
|
|
}
|
|
|
|
function updateCartUI() {
|
|
const total = cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
|
|
const count = cart.reduce((sum, item) => sum + item.quantity, 0);
|
|
document.getElementById('cart-items-count').textContent = count;
|
|
document.getElementById('cart-total-display').textContent = formatCurrency(total);
|
|
document.getElementById('cart-footer').style.display = count > 0 ? 'block' : 'none';
|
|
}
|
|
|
|
function showCart() {
|
|
const list = document.getElementById('cart-list');
|
|
list.innerHTML = '';
|
|
|
|
cart.forEach((item, index) => {
|
|
const div = document.createElement('div');
|
|
div.className = 'd-flex align-items-center gap-3 mb-4';
|
|
div.innerHTML = `
|
|
${item.image ? `<img src="${item.image}" class="cart-item-img">` : `<div class="cart-item-img bg-light d-flex align-items-center justify-content-center text-muted"><i class="bi bi-image"></i></div>`}
|
|
<div class="flex-grow-1">
|
|
<div class="fw-bold text-truncate" style="max-width: 150px;">${item.name}</div>
|
|
<div class="text-primary fw-bold">${formatCurrency(item.unit_price * item.quantity)}</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<button class="quantity-btn" onclick="updateQty(${index}, -1)"><i class="bi bi-dash"></i></button>
|
|
<span class="fw-bold px-1">${item.quantity}</span>
|
|
<button class="quantity-btn" onclick="updateQty(${index}, 1)"><i class="bi bi-plus"></i></button>
|
|
</div>
|
|
`;
|
|
list.appendChild(div);
|
|
});
|
|
|
|
const total = cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
|
|
document.getElementById('modal-subtotal').textContent = formatCurrency(total);
|
|
document.getElementById('modal-total').textContent = formatCurrency(total);
|
|
cartModal.show();
|
|
}
|
|
|
|
function updateQty(index, delta) {
|
|
cart[index].quantity += delta;
|
|
if (cart[index].quantity <= 0) cart.splice(index, 1);
|
|
if (cart.length === 0) cartModal.hide(); else showCart();
|
|
updateCartUI();
|
|
}
|
|
|
|
function placeOrder() {
|
|
if (cart.length === 0) return;
|
|
const btn = document.getElementById('btn-place-order');
|
|
btn.disabled = true;
|
|
btn.innerHTML = `<span class="spinner-border spinner-border-sm me-2"></span>${translations[currentLang].placing_order}`;
|
|
|
|
const total = cart.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
|
|
const customerName = document.getElementById('cust-name').value;
|
|
|
|
fetch('api/order.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
outlet_id: OUTLET_ID,
|
|
table_id: TABLE_ID,
|
|
order_type: 'dine-in',
|
|
customer_name: customerName,
|
|
items: cart,
|
|
total_amount: total
|
|
})
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
Swal.fire({
|
|
title: translations[currentLang].order_placed,
|
|
text: translations[currentLang].order_success_msg,
|
|
icon: 'success',
|
|
confirmButtonText: currentLang === 'ar' ? 'ممتاز' : 'Great!',
|
|
confirmButtonColor: '#2563eb'
|
|
}).then(() => {
|
|
cart = [];
|
|
updateCartUI();
|
|
cartModal.hide();
|
|
document.getElementById('cust-name').value = '';
|
|
});
|
|
} else throw new Error(data.error || 'Failed to place order');
|
|
})
|
|
.catch(err => Swal.fire('Error', err.message, 'error'))
|
|
.finally(() => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = `<span>${translations[currentLang].place_order}</span> <i class="bi bi-arrow-right-short ms-1"></i>`;
|
|
});
|
|
}
|
|
|
|
function showToast(msg) {
|
|
const Toast = Swal.mixin({
|
|
toast: true,
|
|
position: 'top-end',
|
|
showConfirmButton: false,
|
|
timer: 2000,
|
|
timerProgressBar: true
|
|
});
|
|
Toast.fire({ icon: 'success', title: msg });
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|