File manager - Edit - /home/webapp69.cm.in.th/u69319090029/Project SME/order.php
Back
<?php header('Content-Type: text/html; charset=utf-8'); /** * CMTC Tech Solution - Order Gateway & Customer Order Interface (order.php) * Theme: Private CMTC Tech Solution Enterprise UI (Pure Vanilla JS Version) */ require_once 'db.php'; if (session_status() === PHP_SESSION_NONE) { session_start(); } // 1. AJAX JSON API for Order Submission if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (ob_get_length()) ob_clean(); header('Content-Type: application/json; charset=utf-8'); $raw_input = file_get_contents('php://input'); $data = json_decode($raw_input, true); if (!$data) { $data = $_POST; if (isset($_POST['cart_items']) && is_string($_POST['cart_items'])) { $data['cart_items'] = json_decode($_POST['cart_items'], true); } } $store_id = intval($data['store_id'] ?? ($_SESSION['scanned_store_id'] ?? 1)); $table_number = trim($data['table_number'] ?? ($_SESSION['scanned_table'] ?? '1')); $cart_items = $data['cart_items'] ?? []; if ($store_id <= 0) { echo json_encode(['success' => false, 'message' => 'ไม่พบรหัสร้านค้าสำหรับการสั่งซื้อ']); exit; } if (empty($table_number)) { echo json_encode(['success' => false, 'message' => 'กรุณาระบุหมายเลขโต๊ะเพื่อส่งออเดอร์']); exit; } if (empty($cart_items) || !is_array($cart_items)) { echo json_encode(['success' => false, 'message' => 'ไม่มีรายการอาหารในตะกร้าสินค้า']); exit; } // Session-Based Rate Limiting & Cooldown Protection (15-Second Anti-Spam Rule) $cooldown_seconds = 15; $last_order_ts = (int)($_SESSION['last_order_timestamp'] ?? 0); $time_diff = time() - $last_order_ts; if ($last_order_ts > 0 && $time_diff < $cooldown_seconds) { $remaining = $cooldown_seconds - $time_diff; http_response_code(429); echo json_encode([ 'success' => false, 'rate_limited' => true, 'remaining_seconds' => $remaining, 'message' => "กรุณารอสักครู่ ({$remaining} วินาที) ก่อนทำการส่งคำสั่งซื้อเพิ่ม (Anti-Spam Cooldown)" ]); exit; } try { $pdo->beginTransaction(); $payment_method = isset($_POST['payment_method']) ? trim($_POST['payment_method']) : 'cash'; $is_paid = ($payment_method === 'transfer') ? 1 : 0; $raw_voucher = trim($data['voucher'] ?? ($_POST['voucher'] ?? '')); $voucher = !empty($raw_voucher) ? substr($raw_voucher, 0, 50) : null; $stmt = $pdo->prepare("INSERT INTO orders (store_id, table_number, status, payment_method, is_paid, voucher) VALUES (:store_id, :table_number, 'pending', :payment_method, :is_paid, :voucher)"); $stmt->execute([ ':store_id' => $store_id, ':table_number' => $table_number, ':payment_method' => $payment_method, ':is_paid' => $is_paid, ':voucher' => $voucher ]); $order_id = $pdo->lastInsertId(); $stmt_item = $pdo->prepare("INSERT INTO order_items (order_id, menu_id, quantity, price, note, spice_level) VALUES (:order_id, :menu_id, :quantity, :price, :note, :spice_level)"); $stmt_prod = $pdo->prepare("SELECT price FROM menus WHERE menu_id = :id"); foreach ($cart_items as $ci) { $menu_id = intval($ci['id'] ?? ($ci['menu_id'] ?? 0)); $qty = intval($ci['qty'] ?? ($ci['quantity'] ?? 1)); $price = floatval($ci['price'] ?? 0); $note = trim($ci['note'] ?? ''); $spice = trim($ci['spice_level'] ?? 'เผ็ดปกติ'); if ($menu_id <= 0 || $qty <= 0) continue; $stmt_prod->execute([':id' => $menu_id]); $prod = $stmt_prod->fetch(PDO::FETCH_ASSOC); if ($prod) { $price = floatval($prod['price']); } $stmt_item->execute([ ':order_id' => $order_id, ':menu_id' => $menu_id, ':quantity' => $qty, ':price' => $price, ':note' => $note, ':spice_level' => $spice ]); } $pdo->commit(); $_SESSION['last_order_timestamp'] = time(); echo json_encode([ 'success' => true, 'message' => 'สั่งอาหารสำเร็จแล้ว! รายการถูกส่งเข้าห้องครัวเรียบร้อย', 'order_id' => $order_id, 'table_number' => $table_number ]); exit; } catch (Exception $e) { if ($pdo->inTransaction()) $pdo->rollBack(); echo json_encode(['success' => false, 'message' => 'เกิดข้อผิดพลาดในการบันทึกออเดอร์: ' . $e->getMessage()]); exit; } } // 2. Fetch Store & Menu Items for Page Rendering $store_id = intval($_GET['store_id'] ?? ($_GET['shop_id'] ?? ($_SESSION['scanned_store_id'] ?? 1))); $table_number = trim($_GET['table'] ?? ($_SESSION['scanned_table'] ?? '1')); $_SESSION['scanned_store_id'] = $store_id; $_SESSION['scanned_table'] = $table_number; $stmt_store = $pdo->prepare("SELECT * FROM tenants WHERE store_id = :id AND status = 'active'"); $stmt_store->execute([':id' => $store_id]); $store_info = $stmt_store->fetch(PDO::FETCH_ASSOC); if (!$store_info) { $store_info = [ 'store_id' => $store_id, 'store_name' => 'ร้านอาหาร CMTC Tech Solution', 'address' => 'ศูนย์บริการสั่งอาหารออนไลน์', 'custom_logo_url' => 'logo.png', 'store_banner_url' => 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=1200&auto=format&fit=crop&q=80' ]; } $stmt_menus = $pdo->prepare("SELECT menu_id as id, name as menu_name, price, category, description, image_url, is_available, sort_order FROM menus WHERE store_id = :store_id AND is_available = 1 ORDER BY sort_order ASC, menu_id DESC"); $stmt_menus->execute([':store_id' => $store_id]); $menus = $stmt_menus->fetchAll(PDO::FETCH_ASSOC); if (empty($menus)) { $stmt_fallback = $pdo->query("SELECT menu_id as id, name as menu_name, price, category, description, image_url, is_available FROM menus WHERE is_available = 1 LIMIT 10"); $menus = $stmt_fallback->fetchAll(PDO::FETCH_ASSOC); } ?> <!DOCTYPE html> <html lang="th"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo htmlspecialchars($store_info['store_name'] ?? 'ระบบสั่งอาหาร'); ?> - Order Gateway</title> <!-- SweetAlert2 CDN --> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> <!-- Navigation Sound & SweetAlert Engine --> <script src="js/nav_sound_swal.js"></script> <style> :root { --primary: #ff5722; --primary-dark: #e64a19; --bg-color: #f8fafc; --card-bg: #ffffff; --text-dark: #0f172a; --text-muted: #64748b; } body { background-color: var(--bg-color); font-family: 'Sarabun', 'Inter', system-ui, sans-serif; margin: 0; padding: 0; color: var(--text-dark); padding-bottom: 90px; } .header-bar { background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); color: #fff; padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 4px 20px rgba(0,0,0,0.15); position: sticky; top: 0; z-index: 1000; } .store-brand { display: flex; align-items: center; gap: 12px; } .store-logo-icon { width: 40px; height: 40px; border-radius: 10px; object-fit: cover; border: 2px solid var(--primary); background: #fff; } .cart-trigger-btn { position: relative; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 10px 16px; border-radius: 12px; font-weight: bold; cursor: pointer; display: flex; align-items: center; gap: 8px; transition: all 0.2s ease; } .cart-trigger-btn:hover { background: var(--primary); border-color: var(--primary); } .cart-badge { background: var(--primary); color: #fff; font-size: 12px; font-weight: 800; padding: 2px 8px; border-radius: 12px; min-width: 18px; text-align: center; } @media (max-width: 768px) { .cart-trigger-btn { position: fixed !important; bottom: 25px !important; right: 20px !important; z-index: 9999 !important; width: 56px !important; height: 56px !important; padding: 0 !important; border-radius: 50% !important; background: linear-gradient(135deg, #ff5722 0%, #e64a19 100%) !important; color: #ffffff !important; box-shadow: 0 8px 25px rgba(255, 87, 34, 0.5) !important; border: none !important; justify-content: center !important; } .cart-trigger-btn .cart-btn-text { display: none !important; } .cart-trigger-btn .cart-icon-symbol { font-size: 24px !important; } .cart-trigger-btn .cart-badge { position: absolute !important; top: -3px !important; right: -3px !important; background: #0f172a !important; color: #ffffff !important; border: 2px solid #ffffff !important; font-size: 11px !important; font-weight: 800 !important; min-width: 22px !important; height: 22px !important; border-radius: 11px !important; display: flex !important; align-items: center !important; justify-content: center !important; padding: 0 4px !important; box-shadow: 0 2px 6px rgba(0,0,0,0.3) !important; } } .container { max-width: 1100px; margin: 25px auto; padding: 0 20px; } .menu-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 22px; } .menu-card { background: var(--card-bg); border-radius: 16px; overflow: hidden; box-shadow: 0 4px 16px rgba(0,0,0,0.05); border: 1px solid #e2e8f0; display: flex; flex-direction: column; justify-content: space-between; transition: transform 0.2s ease, box-shadow 0.2s ease; } .menu-card:hover { transform: translateY(-4px); box-shadow: 0 10px 25px rgba(0,0,0,0.1); } .menu-img-wrap { width: 100%; height: 160px; position: relative; background: #f1f5f9; cursor: pointer; } .menu-img-wrap img { width: 100%; height: 100%; object-fit: cover; } .menu-body { padding: 16px; flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between; } .menu-title { font-size: 16px; font-weight: 800; margin-bottom: 6px; color: #0f172a; } .menu-desc { font-size: 13px; color: var(--text-muted); margin-bottom: 14px; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .menu-price { font-size: 20px; font-weight: 800; color: var(--primary); margin-bottom: 14px; } .card-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; } .btn-view-detail { background: #f1f5f9; color: #334155; border: 1px solid #cbd5e1; padding: 10px 6px; border-radius: 10px; font-weight: 700; font-size: 12.5px; cursor: pointer; text-align: center; } .btn-add-to-cart { background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); color: #fff; border: none; padding: 10px 6px; border-radius: 10px; font-weight: bold; font-size: 12.5px; cursor: pointer; box-shadow: 0 4px 12px rgba(255,87,34,0.3); display: flex; align-items: center; justify-content: center; gap: 4px; } .btn-add-to-cart:hover { opacity: 0.92; } /* Modal Base Styles */ .modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(15, 23, 42, 0.65); backdrop-filter: blur(6px); z-index: 9999; align-items: center; justify-content: center; } .modal-overlay.active { display: flex; } .modal-box { background: #ffffff; width: 92%; max-width: 500px; border-radius: 20px; box-shadow: 0 20px 40px rgba(0,0,0,0.25); overflow: hidden; display: flex; flex-direction: column; max-height: 88vh; } .modal-header { background: #0f172a; color: #fff; padding: 16px 20px; display: flex; align-items: center; justify-content: space-between; } .modal-header h3 { margin: 0; font-size: 18px; font-weight: 800; color: var(--primary); } .btn-close-modal { background: rgba(255,255,255,0.15); color: #fff; border: none; width: 32px; height: 32px; border-radius: 50%; font-weight: bold; font-size: 16px; cursor: pointer; } .modal-body { padding: 20px; overflow-y: auto; flex-grow: 1; } .modal-footer { padding: 16px 20px; background: #f8fafc; border-top: 1px solid #e2e8f0; display: flex; flex-direction: column; gap: 10px; } .cart-item-row { display: flex; align-items: center; justify-content: space-between; padding: 12px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; margin-bottom: 10px; } .cart-item-info { flex-grow: 1; } .cart-item-title { font-weight: bold; font-size: 14.5px; color: #0f172a; } .cart-item-price { color: var(--primary); font-weight: 800; font-size: 13.5px; } .qty-controls { display: flex; align-items: center; gap: 8px; } .btn-qty { width: 28px; height: 28px; border-radius: 6px; border: none; background: #e2e8f0; font-weight: bold; cursor: pointer; } .btn-qty-plus { background: var(--primary); color: #fff; } .btn-remove-item { background: #ef4444; color: #fff; border: none; padding: 4px 8px; border-radius: 6px; font-size: 11px; font-weight: bold; cursor: pointer; margin-left: 6px; } .total-row { display: flex; justify-content: space-between; font-size: 18px; font-weight: 800; color: #0f172a; } .btn-confirm-order { width: 100%; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #fff; border: none; padding: 14px; border-radius: 12px; font-weight: 800; font-size: 16px; cursor: pointer; box-shadow: 0 4px 15px rgba(16,185,129,0.3); } .toast-msg { position: fixed; top: 75px; right: 20px; background: #0f172a; color: #fff; padding: 12px 18px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); border-left: 4px solid var(--primary); z-index: 10000; font-weight: bold; font-size: 14px; display: none; } </style> </head> <body> <!-- Header Bar --> <div class="header-bar"> <div class="store-brand"> <img src="<?php echo htmlspecialchars($store_info['custom_logo_url'] ?: 'logo.png'); ?>" class="store-logo-icon" alt="Logo" onerror="this.src='logo.png'"> <div> <div style="font-weight: 800; font-size: 16px;"><?php echo htmlspecialchars($store_info['store_name']); ?></div> <div style="font-size: 12px; color: #94a3b8;">โต๊ะที่ <?php echo htmlspecialchars($table_number); ?> • สั่งอาหารออนไลน์</div> </div> </div> <button type="button" class="cart-trigger-btn" id="cart-icon-btn" aria-label="Cart"> <span class="cart-icon-symbol">🛒</span> <span class="cart-btn-text">ตะกร้าสินค้า</span> <span class="cart-badge" id="cart-badge">0</span> </button> </div> <!-- Main Container --> <div class="container"> <h2 style="font-size: 22px; font-weight: 800; margin-bottom: 20px; color: #0f172a;">🍽️ เมนูอาหารพร้อมเสิร์ฟ</h2> <div class="menu-grid"> <?php foreach ($menus as $m): $img = !empty($m['image_url']) ? $m['image_url'] : 'https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=600&auto=format&fit=crop&q=80'; ?> <div class="menu-card"> <div class="menu-img-wrap btn-view-detail" data-id="<?php echo $m['id']; ?>" data-name="<?php echo htmlspecialchars($m['menu_name'], ENT_QUOTES); ?>" data-price="<?php echo $m['price']; ?>" data-detail="<?php echo htmlspecialchars($m['description'] ?: 'เมนูคุณภาพ ปรุงสดใหม่ทุกจาน', ENT_QUOTES); ?>" data-img="<?php echo htmlspecialchars($img, ENT_QUOTES); ?>"> <img src="<?php echo htmlspecialchars($img); ?>" alt="<?php echo htmlspecialchars($m['menu_name']); ?>" onerror="this.src='https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=600&auto=format&fit=crop&q=80'" loading="lazy" decoding="async"> </div> <div class="menu-body"> <div> <div class="menu-title"><?php echo htmlspecialchars($m['menu_name']); ?></div> <div class="menu-desc"><?php echo htmlspecialchars($m['description'] ?: 'เมนูคุณภาพ ปรุงสดใหม่ทุกจาน'); ?></div> </div> <div> <div class="menu-price">฿<?php echo number_format($m['price'], 2); ?></div> <div class="card-actions"> <button type="button" class="btn-add-to-cart" id="add-item" data-id="<?php echo $m['id']; ?>" data-name="<?php echo htmlspecialchars($m['menu_name'], ENT_QUOTES); ?>" data-price="<?php echo $m['price']; ?>" data-detail="<?php echo htmlspecialchars($m['description'] ?: 'เมนูคุณภาพ ปรุงสดใหม่ทุกจาน', ENT_QUOTES); ?>" data-img="<?php echo htmlspecialchars($img, ENT_QUOTES); ?>" style="width: 100%;"> 🛒 เพิ่มลงตะกร้า </button> </div> </div> </div> </div> <?php endforeach; ?> </div> </div> <!-- 1. FOOD DETAIL MODAL --> <div class="modal-overlay" id="detail-modal"> <div class="modal-box"> <div class="modal-header"> <h3 id="detail-modal-title">รายละเอียดอาหาร</h3> <button type="button" class="btn-close-modal" id="btn-close-detail">✕</button> </div> <div class="modal-body"> <img id="detail-modal-img" src="" style="width: 100%; height: 180px; object-fit: cover; border-radius: 12px; margin-bottom: 15px;" alt="Food Image"> <div id="detail-modal-desc" style="font-size: 14px; color: #334155; background: #f8fafc; padding: 12px; border-radius: 8px; border: 1px solid #e2e8f0; margin-bottom: 15px; line-height: 1.5;"></div> <div style="display: flex; justify-content: space-between; align-items: center;"> <div style="font-size: 22px; font-weight: 800; color: var(--primary);" id="detail-modal-price">฿0.00</div> <button type="button" class="btn-add-to-cart" id="btn-detail-add" style="width: auto; padding: 10px 20px;">🛒 สั่งรายการนี้</button> </div> </div> </div> </div> <!-- 2. CART DRAWER MODAL --> <div class="modal-overlay" id="cart-modal"> <div class="modal-box" id="cartDrawer"> <div class="modal-header"> <h3>🛒 ตะกร้าสินค้าของคุณ</h3> <button type="button" class="btn-close-modal" id="btn-close-cart">✕</button> </div> <div class="modal-body" id="modal-cart-items"> <div style="text-align: center; color: #64748b; padding: 40px 0;">ไม่มีรายการอาหารในตะกร้า</div> </div> <div class="modal-footer"> <input type="hidden" id="cart_table_number" value="<?php echo htmlspecialchars($table_number); ?>"> <div class="total-row"> <span>ยอดรวมสุทธิ:</span> <span style="color: var(--primary);">฿<span id="cart-total-price">0.00</span></span> </div> <button type="button" class="btn-confirm-order" id="btn-confirm-order"> ✅ ยืนยันการสั่งซื้ออาหาร </button> </div> </div> </div> <!-- Toast Notice Box --> <div class="toast-msg" id="toast-msg"></div> <script> // Global Cart Array & Store Variables let cart = []; const currentStoreId = <?php echo $store_id; ?>; let activeDetailItem = null; // Safe Event Listener Binding Logic (Vanilla JS) document.addEventListener('DOMContentLoaded', function() { // 1. Food Detail Modal Trigger document.querySelectorAll('.btn-view-detail').forEach(function(elem) { elem.addEventListener('click', function(e) { e.preventDefault(); const id = parseInt(this.getAttribute('data-id') || 0); const name = this.getAttribute('data-name') || 'รายละเอียดอาหาร'; const price = parseFloat(this.getAttribute('data-price') || 0); const detail = this.getAttribute('data-detail') || 'เมนูคุณภาพ ปรุงสดใหม่ทุกจาน'; const img = this.getAttribute('data-img') || 'https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=600&auto=format&fit=crop&q=80'; activeDetailItem = { id: id, name: name, price: price }; const titleEl = document.getElementById('detail-modal-title'); const descEl = document.getElementById('detail-modal-desc'); const priceEl = document.getElementById('detail-modal-price'); const imgEl = document.getElementById('detail-modal-img'); const detailModal = document.getElementById('detail-modal'); if (titleEl) titleEl.textContent = name; if (descEl) descEl.textContent = detail; if (priceEl) priceEl.textContent = '฿' + price.toFixed(2); if (imgEl) imgEl.src = img; if (detailModal) { detailModal.style.display = 'flex'; detailModal.classList.add('active'); } }); }); // Close Detail Modal Button const btnCloseDetail = document.getElementById('btn-close-detail'); if (btnCloseDetail) { btnCloseDetail.addEventListener('click', function(e) { e.preventDefault(); closeModal('detail-modal'); }); } // Add to Cart from Detail Modal const btnDetailAdd = document.getElementById('btn-detail-add'); if (btnDetailAdd) { btnDetailAdd.addEventListener('click', function(e) { e.preventDefault(); if (activeDetailItem) { addToCart(activeDetailItem.id, activeDetailItem.name, activeDetailItem.price, 1); closeModal('detail-modal'); } }); } // 2. Add to Cart Buttons (.btn-add-to-cart & #add-item) document.querySelectorAll('.btn-add-to-cart, #add-item').forEach(function(button) { if (button.id === 'btn-detail-add') return; // Skip detail modal button button.addEventListener('click', function(e) { e.preventDefault(); const id = parseInt(this.getAttribute('data-id') || 0); const name = this.getAttribute('data-name') || 'รายการอาหาร'; const price = parseFloat(this.getAttribute('data-price') || 0); const detail = this.getAttribute('data-detail') || 'เมนูคุณภาพ ปรุงสดใหม่ทุกจาน'; const img = this.getAttribute('data-img') || 'https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=600&auto=format&fit=crop&q=80'; activeDetailItem = { id: id, name: name, price: price }; const titleEl = document.getElementById('detail-modal-title'); const descEl = document.getElementById('detail-modal-desc'); const priceEl = document.getElementById('detail-modal-price'); const imgEl = document.getElementById('detail-modal-img'); const detailModal = document.getElementById('detail-modal'); if (titleEl) titleEl.textContent = name; if (descEl) descEl.textContent = detail; if (priceEl) priceEl.textContent = '฿' + price.toFixed(2); if (imgEl) imgEl.src = img; if (detailModal) { detailModal.style.display = 'flex'; detailModal.classList.add('active'); } }); }); // Cart Modal Controls const cartIconBtn = document.getElementById('cart-icon-btn'); const btnCloseCart = document.getElementById('btn-close-cart'); if (cartIconBtn) { cartIconBtn.addEventListener('click', function(e) { e.preventDefault(); openCartModal(); }); } if (btnCloseCart) { btnCloseCart.addEventListener('click', function(e) { e.preventDefault(); closeModal('cart-modal'); }); } // Overlay backdrop click to close document.querySelectorAll('.modal-overlay').forEach(function(overlay) { overlay.addEventListener('click', function(e) { if (e.target === this) { closeModal(this.id); } }); }); // 3. Confirm Order Submit Button const btnConfirmOrder = document.getElementById('btn-confirm-order'); if (btnConfirmOrder) { btnConfirmOrder.addEventListener('click', function(e) { e.preventDefault(); submitOrderAJAX(); }); } }); // Helper Functions function closeModal(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.style.display = 'none'; modal.classList.remove('active'); } } function openCartModal() { updateCartUI(); const modal = document.getElementById('cart-modal'); if (modal) { modal.style.display = 'flex'; modal.classList.add('active'); } } // Add Item to Array Logic function addToCart(id, name, price, qty) { const targetId = parseInt(id); const targetQty = parseInt(qty) || 1; const existing = cart.find(item => item.id === targetId); if (existing) { existing.qty += targetQty; } else { cart.push({ id: targetId, name: name, price: parseFloat(price), qty: targetQty }); } updateCartUI(); showToast(`🎉 เพิ่ม "${name}" (x${targetQty}) ลงตะกร้าแล้ว!`); } // Render Cart UI & Badge Counter function updateCartUI() { const cartBadge = document.getElementById('cart-badge'); const container = document.getElementById('modal-cart-items'); const totalPriceEl = document.getElementById('cart-total-price'); let totalQty = 0; let totalPrice = 0; cart.forEach(item => { totalQty += item.qty; totalPrice += item.price * item.qty; }); if (cartBadge) { cartBadge.textContent = totalQty; } if (totalPriceEl) { totalPriceEl.textContent = totalPrice.toLocaleString('th-TH', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } if (container) { if (cart.length === 0) { container.innerHTML = '<div style="text-align: center; color: #64748b; padding: 40px 0;">ไม่มีรายการอาหารในตะกร้า</div>'; return; } let html = ''; cart.forEach((item, index) => { html += ` <div class="cart-item-row"> <div class="cart-item-info"> <div class="cart-item-title">${escapeHTML(item.name)}</div> <div class="cart-item-price">฿${(item.price * item.qty).toFixed(2)} (${item.price.toFixed(2)} x ${item.qty})</div> </div> <div class="qty-controls"> <button type="button" class="btn-qty" onclick="changeCartQty(${index}, -1)">-</button> <span style="font-weight: bold; font-size: 14px; width: 18px; text-align: center;">${item.qty}</span> <button type="button" class="btn-qty btn-qty-plus" onclick="changeCartQty(${index}, 1)">+</button> <button type="button" class="btn-remove-item" onclick="removeCartItem(${index})">ลบ</button> </div> </div> `; }); container.innerHTML = html; } } function changeCartQty(index, delta) { if (cart[index]) { cart[index].qty += delta; if (cart[index].qty <= 0) { cart.splice(index, 1); } updateCartUI(); } } function removeCartItem(index) { if (cart[index]) { cart.splice(index, 1); updateCartUI(); } } function escapeHTML(str) { return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); } function showToast(message) { const toast = document.getElementById('toast-msg'); if (toast) { toast.textContent = message; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 2000); } } // 3. AJAX Submit Order to PHP function submitOrderAJAX() { if (cart.length === 0) { alert('กรุณาเลือกรายการอาหารอย่างน้อย 1 รายการก่อนส่งสั่งซื้อ'); return; } const tableInput = document.getElementById('cart_table_number'); const table_number = tableInput ? tableInput.value.trim() : '1'; if (!table_number) { alert('กรุณาระบุหมายเลขโต๊ะอาหารก่อนส่งสั่งซื้อ'); return; } const payload = { action: 'place_order', store_id: currentStoreId, table_number: table_number, cart_items: cart }; const btnConfirm = document.getElementById('btn-confirm-order'); if (btnConfirm) { btnConfirm.disabled = true; btnConfirm.textContent = '⏳ กำลังส่งออเดอร์เข้าห้องครัว...'; } fetch('order.php', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(payload) }) .then(async function(response) { const text = await response.text(); try { return JSON.parse(text); } catch(e) { console.error("Raw response:", text); throw new Error("Server JSON Response Error: " + text.substring(0, 100)); } }) .then(function(data) { if (btnConfirm) { btnConfirm.disabled = false; btnConfirm.textContent = '✅ ยืนยันการสั่งซื้ออาหาร'; } if (data.success) { alert('🎉 สั่งอาหารสำเร็จแล้ว!\nหมายเลขออเดอร์: #' + data.order_id + '\nโต๊ะที่: ' + data.table_number + '\nรายการถูกส่งเข้าห้องครัวเรียบร้อย'); cart = []; updateCartUI(); closeModal('cart-modal'); } else { alert('ไม่สามารถส่งออเดอร์ได้: ' + (data.message || 'เกิดข้อผิดพลาดในการสั่งซื้อ')); } }) .catch(function(error) { if (btnConfirm) { btnConfirm.disabled = false; btnConfirm.textContent = '✅ ยืนยันการสั่งซื้ออาหาร'; } console.error("Submit Error:", error); alert('เกิดข้อผิดพลาดทางเทคนิค: ' + error.message); }); } </script> </body> </html>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.38 |
proxy
|
phpinfo
|
Settings