File manager - Edit - /home/webapp69.cm.in.th/u69319090001/Shop 001/admin.php
Back
<?php /** * หน้าคอนโซลผู้ดูแลระบบ (Admin Console) * จัดการสินค้า, จัดการสต็อก (Limit & Made-to-order), ตรวจสอบสลิป, สรุปยอดผลิตส่งโรงงาน และอัปเดตเลขพัสดุกลุ่ม (CSV) */ session_start(); require_once 'db.php'; // รหัสผ่านเข้าสู่ระบบผู้ดูแลระบบที่ผ่านการเข้ารหัสแบบ Bcrypt // รหัสผ่านต้นฉบับคือ: '11223344' $admin_password_hash = '$2y$10$Dc10sv8mOXS29Ymk1BEkb.Uxrd6rU0DH0chevazBAVqbkZTKWZTvW'; // บังคับใช้งาน HTTPS (ข้ามเมื่ออยู่บน localhost/127.0.0.1 หรือวงแลน) if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') { if ($_SERVER['HTTP_HOST'] !== 'localhost' && $_SERVER['HTTP_HOST'] !== '127.0.0.1' && strpos($_SERVER['HTTP_HOST'], '192.168.') === false) { header("Location: https://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); exit; } } // จัดการการเข้าระบบ / ออกจากระบบ if (isset($_POST['login'])) { $password = isset($_POST['password']) ? $_POST['password'] : ''; if (password_verify($password, $admin_password_hash)) { $_SESSION['admin_logged'] = true; } else { $login_error = "รหัสผ่านไม่ถูกต้อง! กรุณาลองใหม่อีกครั้ง"; } } if (isset($_GET['logout'])) { unset($_SESSION['admin_logged']); header("Location: admin.php"); exit; } $is_logged = isset($_SESSION['admin_logged']) && $_SESSION['admin_logged'] === true; // หากไม่ได้เข้าระบบ ให้แสดงหน้า Login if (!$is_logged): ?> <!DOCTYPE html> <html lang="th"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin Login - ระบบจองสินค้า</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> body { background-color: #f7fafc; font-family: 'Noto Sans Thai', sans-serif; height: 100vh; display: flex; align-items: center; justify-content: center; } .login-card { border-radius: 16px; border: none; box-shadow: 0 10px 25px rgba(0,0,0,0.05); width: 100%; max-width: 400px; } .btn-red { background-color: #e61e2b; color: white; border-radius: 8px; border: none; padding: 10px; } .btn-red:hover { background-color: #c1101b; color: white; } .logo-header { color: #e61e2b; font-weight: 700; font-size: 1.8rem; } </style> </head> <body> <div class="card login-card p-4"> <div class="card-body"> <div class="text-center mb-4"> <span class="logo-header"><i class="fa-solid fa-user-shield me-2"></i>Admin Panel</span> <p class="text-muted mt-1">ระบบจองสินค้าออนไลน์</p> </div> <?php if (isset($login_error)): ?> <div class="alert alert-danger py-2" role="alert"> <i class="fa-solid fa-circle-exclamation me-1"></i> <?php echo $login_error; ?> </div> <?php endif; ?> <form action="admin.php" method="POST"> <div class="mb-3"> <label for="password" class="form-label font-weight-bold">รหัสผ่านผู้ดูแลระบบ</label> <input type="password" class="form-control" id="password" name="password" required placeholder="ใส่รหัสผ่านเดโม: admin"> </div> <button type="submit" name="login" class="btn btn-red w-100 mt-2"> <i class="fa-solid fa-right-to-bracket me-1"></i> เข้าสู่ระบบ </button> <div class="text-center mt-3"> <a href="index.php" class="text-decoration-none text-muted small"><i class="fa-solid fa-arrow-left me-1"></i> กลับไปหน้าแรกของร้าน</a> </div> </form> </div> </div> </body> </html> <?php exit; endif; // ========================================== // ส่วนประมวลผล ACTIONS ของ ADMIN (เมื่อล็อกอินแล้ว) // ========================================== $action = isset($_GET['action']) ? $_GET['action'] : ''; // 1. เพิ่มสินค้าใหม่ if ($action === 'add_product' && $_SERVER['REQUEST_METHOD'] === 'POST') { $name = trim(htmlspecialchars($_POST['name'])); $category = !empty(trim($_POST['category'])) ? trim(htmlspecialchars($_POST['category'])) : 'ทั่วไป'; $description = trim(htmlspecialchars($_POST['description'])); $price = (float)$_POST['price']; $video_url = !empty(trim($_POST['video_url'])) ? trim(htmlspecialchars($_POST['video_url'])) : null; $start_time = !empty($_POST['start_time']) ? $_POST['start_time'] : null; $end_time = !empty($_POST['end_time']) ? $_POST['end_time'] : null; // จัดการรูปภาพสินค้า (รองรับทั้งการใส่ลิงก์รูปภาพโดยตรง และการอัปโหลดไฟล์) $image_url = !empty(trim($_POST['image_url'])) ? trim($_POST['image_url']) : 'assets/images/default.png'; if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $file = $_FILES['image']; $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (in_array($ext, $allowed)) { $filename = 'product_' . uniqid() . '.' . $ext; $upload_dir = __DIR__ . '/assets/images/'; if (!is_dir($upload_dir)) { @mkdir($upload_dir, 0777, true); } if (move_uploaded_file($file['tmp_name'], $upload_dir . $filename)) { $image_url = 'assets/images/' . $filename; } } } try { $stmt = $pdo->prepare("INSERT INTO products (name, category, description, price, image_url, video_url, start_time, end_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$name, $category, $description, $price, $image_url, $video_url, $start_time, $end_time]); $_SESSION['admin_success'] = "เพิ่มสินค้าใหม่เรียบร้อยแล้ว"; } catch (PDOException $e) { $_SESSION['admin_error'] = "เกิดข้อผิดพลาด: " . $e->getMessage(); } header("Location: admin.php?tab=products"); exit; } // 2. แก้ไขสินค้า if ($action === 'edit_product' && $_SERVER['REQUEST_METHOD'] === 'POST') { $id = (int)$_POST['id']; $name = trim(htmlspecialchars($_POST['name'])); $category = !empty(trim($_POST['category'])) ? trim(htmlspecialchars($_POST['category'])) : 'ทั่วไป'; $description = trim(htmlspecialchars($_POST['description'])); $price = (float)$_POST['price']; $video_url = !empty(trim($_POST['video_url'])) ? trim(htmlspecialchars($_POST['video_url'])) : null; $start_time = !empty($_POST['start_time']) ? $_POST['start_time'] : null; $end_time = !empty($_POST['end_time']) ? $_POST['end_time'] : null; $input_image_url = !empty(trim($_POST['image_url'])) ? trim($_POST['image_url']) : null; try { // อัปเดตข้อมูลทั่วไป $stmt = $pdo->prepare("UPDATE products SET name = ?, category = ?, description = ?, price = ?, video_url = ?, start_time = ?, end_time = ? WHERE id = ?"); $stmt->execute([$name, $category, $description, $price, $video_url, $start_time, $end_time, $id]); // ถ้ามีการระบุลิงก์รูปภาพมา if ($input_image_url !== null) { $update_img = $pdo->prepare("UPDATE products SET image_url = ? WHERE id = ?"); $update_img->execute([$input_image_url, $id]); } // จัดการอัปโหลดรูปภาพสินค้าใหม่ (ถ้ามีการแนบไฟล์มา ให้ความสำคัญกับไฟล์อัปโหลด) if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { $file = $_FILES['image']; $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (in_array($ext, $allowed)) { $filename = 'product_' . uniqid() . '.' . $ext; $upload_dir = __DIR__ . '/assets/images/'; if (!is_dir($upload_dir)) { @mkdir($upload_dir, 0777, true); } if (move_uploaded_file($file['tmp_name'], $upload_dir . $filename)) { $image_url = 'assets/images/' . $filename; // ลบรูปภาพเก่า (ยกเว้นรูปภาพตั้งต้นหรือ external URL) $old_stmt = $pdo->prepare("SELECT image_url FROM products WHERE id = ?"); $old_stmt->execute([$id]); $old_img = $old_stmt->fetchColumn(); if ($old_img && $old_img !== 'assets/images/default.png' && !filter_var($old_img, FILTER_VALIDATE_URL) && file_exists(__DIR__ . '/' . $old_img)) { @unlink(__DIR__ . '/' . $old_img); } // อัปเดตพาธรูปใหม่ $update_img = $pdo->prepare("UPDATE products SET image_url = ? WHERE id = ?"); $update_img->execute([$image_url, $id]); } } } $_SESSION['admin_success'] = "แก้ไขรายละเอียดสินค้าเรียบร้อย"; } catch (PDOException $e) { $_SESSION['admin_error'] = "เกิดข้อผิดพลาด: " . $e->getMessage(); } header("Location: admin.php?tab=products"); exit; } // 3. ลบสินค้า if ($action === 'delete_product') { $id = (int)$_GET['id']; try { // ลบรูปภาพของสินค้านั้นๆ ก่อน $stmt_img = $pdo->prepare("SELECT image_url FROM products WHERE id = ?"); $stmt_img->execute([$id]); $img = $stmt_img->fetchColumn(); if ($img && $img !== 'assets/images/default.png' && file_exists(__DIR__ . '/' . $img)) { unlink(__DIR__ . '/' . $img); } // ทำการลบข้อมูลสินค้า (ตาราง Variants และ Bookings จะถูกลบอัตโนมัติด้วย CASCADE) $stmt = $pdo->prepare("DELETE FROM products WHERE id = ?"); $stmt->execute([$id]); $_SESSION['admin_success'] = "ลบสินค้าและข้อมูลที่เกี่ยวข้องเรียบร้อย"; } catch (PDOException $e) { $_SESSION['admin_error'] = "ไม่สามารถลบสินค้าได้: " . $e->getMessage(); } header("Location: admin.php?tab=products"); exit; } // 3.1 เปลี่ยนชื่อหมวดหมู่สินค้าทั้งหมด (Rename Category) if ($action === 'rename_category' && $_SERVER['REQUEST_METHOD'] === 'POST') { $old_cat = trim(htmlspecialchars($_POST['old_category'])); $new_cat = trim(htmlspecialchars($_POST['new_category'])); if (!empty($old_cat) && !empty($new_cat)) { try { $stmt = $pdo->prepare("UPDATE products SET category = ? WHERE category = ?"); $stmt->execute([$new_cat, $old_cat]); $_SESSION['admin_success'] = "เปลี่ยนชื่อหมวดหมู่จาก '{$old_cat}' เป็น '{$new_cat}' เรียบร้อยแล้ว"; } catch (PDOException $e) { $_SESSION['admin_error'] = "เกิดข้อผิดพลาด: " . $e->getMessage(); } } header("Location: admin.php?tab=products"); exit; } // 4. เพิ่มตัวเลือกสินค้า (Add Variant) if ($action === 'add_variant' && $_SERVER['REQUEST_METHOD'] === 'POST') { $product_id = (int)$_POST['product_id']; $variant_name = trim(htmlspecialchars($_POST['variant_name'])); $stock = (int)$_POST['stock']; $is_unlimited = isset($_POST['is_unlimited']) ? 1 : 0; try { $stmt = $pdo->prepare("INSERT INTO product_variants (product_id, variant_name, stock, is_unlimited) VALUES (?, ?, ?, ?)"); $stmt->execute([$product_id, $variant_name, $stock, $is_unlimited]); $_SESSION['admin_success'] = "เพิ่มตัวเลือกสินค้าเรียบร้อย"; } catch (PDOException $e) { $_SESSION['admin_error'] = "เกิดข้อผิดพลาด: " . $e->getMessage(); } header("Location: admin.php?tab=products"); exit; } // 5. ลบตัวเลือกสินค้า (Delete Variant) if ($action === 'delete_variant') { $id = (int)$_GET['id']; try { $stmt = $pdo->prepare("DELETE FROM product_variants WHERE id = ?"); $stmt->execute([$id]); $_SESSION['admin_success'] = "ลบตัวเลือกสินค้าเรียบร้อย"; } catch (PDOException $e) { $_SESSION['admin_error'] = "ไม่สามารถลบตัวเลือกสินค้าได้: " . $e->getMessage(); } header("Location: admin.php?tab=products"); exit; } // 6. อนุมัติการโอนเงิน (Approve Payment) if ($action === 'approve_booking') { $id = (int)$_GET['id']; try { $stmt = $pdo->prepare("UPDATE bookings SET payment_status = 'Approved', status = 'Confirmed' WHERE id = ?"); $stmt->execute([$id]); $_SESSION['admin_success'] = "อนุมัติสลิปการโอนเงินและยืนยันการจองเรียบร้อย"; } catch (PDOException $e) { $_SESSION['admin_error'] = "เกิดข้อผิดพลาด: " . $e->getMessage(); } header("Location: admin.php?tab=orders"); exit; } // 7. ปฏิเสธการโอนเงิน (Reject Payment & คืนสต็อก) if ($action === 'reject_booking') { $id = (int)$_GET['id']; try { $pdo->beginTransaction(); // 1. ดึงรายละเอียดการจองก่อนเพื่อคืนสต็อก (หากไม่ใช่ Made-to-order) $stmt_booking = $pdo->prepare("SELECT b.variant_id, b.quantity, b.payment_status, pv.is_unlimited FROM bookings b INNER JOIN product_variants pv ON b.variant_id = pv.id WHERE b.id = ? FOR UPDATE"); $stmt_booking->execute([$id]); $booking = $stmt_booking->fetch(); if ($booking) { // คืนสต็อกเฉพาะในกรณีที่สินค้าไม่ใช้ Made-to-Order และสถานะเดิมไม่ใช่ Rejected อยู่แล้ว if ((int)$booking['is_unlimited'] === 0 && $booking['payment_status'] !== 'Rejected') { $update_stock = $pdo->prepare("UPDATE product_variants SET stock = stock + ? WHERE id = ?"); $update_stock->execute([$booking['quantity'], $booking['variant_id']]); } // 2. ปรับปรุงสถานะการจองเป็น Rejected / Cancelled $update_booking = $pdo->prepare("UPDATE bookings SET payment_status = 'Rejected', status = 'Cancelled' WHERE id = ?"); $update_booking->execute([$id]); } $pdo->commit(); $_SESSION['admin_success'] = "ปฏิเสธสลิปเรียบร้อย (ระบบได้คืนค่าจำนวนสต็อกสินค้าแล้ว)"; } catch (Exception $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $_SESSION['admin_error'] = "เกิดข้อผิดพลาดในการปฏิเสธการจอง: " . $e->getMessage(); } header("Location: admin.php?tab=orders"); exit; } // 8. อัปเดตเลขพัสดุรายบุคคล (Update Single Tracking) if ($action === 'update_tracking' && $_SERVER['REQUEST_METHOD'] === 'POST') { $id = (int)$_POST['booking_id']; $tracking = trim(htmlspecialchars($_POST['tracking_number'])); try { $stmt = $pdo->prepare("UPDATE bookings SET tracking_number = ? WHERE id = ?"); $stmt->execute([$tracking, $id]); $_SESSION['admin_success'] = "อัปเดตเลขพัสดุเรียบร้อย"; } catch (PDOException $e) { $_SESSION['admin_error'] = "เกิดข้อผิดพลาด: " . $e->getMessage(); } header("Location: admin.php?tab=orders"); exit; } // 9. อัปเดตเลขพัสดุแบบกลุ่มผ่านไฟล์ CSV (Bulk Upload Tracking) if ($action === 'bulk_tracking' && $_SERVER['REQUEST_METHOD'] === 'POST') { if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === UPLOAD_ERR_OK) { $file = $_FILES['csv_file']; $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if ($ext !== 'csv') { $_SESSION['admin_error'] = "กรุณาอัปโหลดเฉพาะไฟล์นามสกุล .csv เท่านั้น"; } else { $handle = fopen($file['tmp_name'], 'r'); $updated = 0; $errors = []; // อ่านบรรทัดแรก (Header) เพื่อข้ามหัวข้อคอลัมน์ fgetcsv($handle); while (($row = fgetcsv($handle)) !== false) { if (count($row) >= 2) { $booking_id = (int)$row[0]; $tracking = trim(htmlspecialchars($row[1])); if ($booking_id > 0 && !empty($tracking)) { try { $stmt = $pdo->prepare("UPDATE bookings SET tracking_number = ? WHERE id = ?"); $stmt->execute([$tracking, $booking_id]); $updated++; } catch (PDOException $e) { $errors[] = "แถวที่ได้รับจอง ID #{$booking_id}: เกิดข้อผิดพลาด " . $e->getMessage(); } } } } fclose($handle); $_SESSION['admin_success'] = "อัปโหลดกลุ่มเสร็จสิ้น! อัปเดตเลขพัสดุสำเร็จ {$updated} รายการ"; if (count($errors) > 0) { $_SESSION['admin_bulk_errors'] = $errors; } } } else { $_SESSION['admin_error'] = "การอัปโหลดไฟล์ล้มเหลว กรุณาเลือกไฟล์อีกครั้ง"; } header("Location: admin.php?tab=orders"); exit; } // ดึงข้อมูลสำหรับหน้าเว็บ $active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'dashboard'; // ดึงสถิติ Dashboard $stat_total_bookings = $pdo->query("SELECT COUNT(*) FROM bookings")->fetchColumn(); $stat_approved_bookings = $pdo->query("SELECT COUNT(*) FROM bookings WHERE payment_status = 'Approved'")->fetchColumn(); $stat_pending_slips = $pdo->query("SELECT COUNT(*) FROM bookings WHERE payment_status = 'Pending'")->fetchColumn(); $stat_revenue = $pdo->query("SELECT SUM(total_price) FROM bookings WHERE payment_status = 'Approved'")->fetchColumn() ?: 0; // รายงานสำหรับส่งโรงงาน (Factory Order Report) $factory_orders = $pdo->query(" SELECT p.name AS product_name, pv.variant_name, SUM(b.quantity) AS total_qty, COUNT(b.id) AS total_orders FROM bookings b INNER JOIN products p ON b.product_id = p.id INNER JOIN product_variants pv ON b.variant_id = pv.id WHERE b.payment_status = 'Approved' GROUP BY b.product_id, b.variant_id ORDER BY p.name ASC, pv.variant_name ASC ")->fetchAll(); // รายงานรายได้แยกตามประเภทสินค้า $product_revenues = $pdo->query(" SELECT p.name, SUM(b.total_price) AS revenue FROM bookings b INNER JOIN products p ON b.product_id = p.id WHERE b.payment_status = 'Approved' GROUP BY b.product_id ")->fetchAll(); // รายการสินค้า $products_list = $pdo->query("SELECT * FROM products ORDER BY id DESC")->fetchAll(); // ดึงหมวดหมู่สินค้าทั้งหมดที่มีอยู่ในระบบ $existing_categories = $pdo->query("SELECT DISTINCT category FROM products WHERE category IS NOT NULL AND category != '' ORDER BY category ASC")->fetchAll(PDO::FETCH_COLUMN); if (empty($existing_categories)) { $existing_categories = ['เสื้อผ้าและเครื่องแต่งกาย', 'กระเป๋าและอุปกรณ์', 'ของสะสมและของที่ระลึก', 'เครื่องเขียนและอุปกรณ์การเรียน', 'ทั่วไป']; } // รายการตัวเลือกสินค้าทั้งหมด $variants_list = []; if (!empty($products_list)) { $variants_data = $pdo->query("SELECT * FROM product_variants ORDER BY product_id, id ASC")->fetchAll(); foreach ($variants_data as $v) { $variants_list[$v['product_id']][] = $v; } } // รายการจองทั้งหมด $bookings_list = $pdo->query(" SELECT b.*, p.name AS product_name, p.price AS single_price, pv.variant_name FROM bookings b INNER JOIN products p ON b.product_id = p.id INNER JOIN product_variants pv ON b.variant_id = pv.id ORDER BY b.id DESC ")->fetchAll(); ?> <!DOCTYPE html> <html lang="th"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Admin Dashboard - ระบบจัดการ Pre-order</title> <!-- Google Fonts --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Noto+Sans+Thai:wght@300;400;500;700&display=swap" rel="stylesheet"> <!-- Bootstrap 5 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <!-- FontAwesome for Icons --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> :root { --primary-red: #e61e2b; --hover-red: #c1101b; --light-red: #ffebeb; --dark-gray: #2d3748; --light-gray: #f7fafc; --border-color: #edf2f7; --font-family: 'Noto Sans Thai', 'Inter', sans-serif; } body { font-family: var(--font-family); background-color: #f8f9fa; color: var(--dark-gray); } .admin-navbar { background-color: #fff; border-bottom: 2px solid var(--primary-red); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05); } .admin-brand { font-weight: 700; color: var(--primary-red); } /* Sidebar Tabs styling */ .admin-sidebar { background-color: #fff; border-radius: 12px; border: 1px solid var(--border-color); padding: 1rem; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02); } .nav-pills .nav-link { color: #4a5568; font-weight: 500; border-radius: 8px; padding: 12px 16px; margin-bottom: 5px; transition: all 0.2s; } .nav-pills .nav-link:hover { background-color: var(--light-red); color: var(--primary-red); } .nav-pills .nav-link.active { background-color: var(--primary-red); color: #fff; box-shadow: 0 4px 6px rgba(230, 30, 43, 0.25); } .stat-card { border: none; border-radius: 16px; box-shadow: 0 4px 10px rgba(0,0,0,0.02); transition: transform 0.2s; background-color: white; } .stat-card:hover { transform: translateY(-3px); } .btn-red { background-color: var(--primary-red); color: white; font-weight: 600; border: none; border-radius: 8px; padding: 8px 16px; } .btn-red:hover { background-color: var(--hover-red); color: white; } .btn-sm { padding: 6px 12px; font-size: 0.85rem; } .product-list-img { width: 60px; height: 60px; object-fit: cover; border-radius: 8px; background-color: #f7fafc; } /* Status colors */ .status-badge-pending { background-color: #fef3c7; color: #d97706; } .status-badge-approved { background-color: #d1fae5; color: #059669; } .status-badge-rejected { background-color: #fee2e2; color: #dc2626; } .slip-thumbnail { width: 50px; height: 50px; object-fit: cover; border-radius: 6px; cursor: pointer; transition: opacity 0.2s; border: 1px solid var(--border-color); } .slip-thumbnail:hover { opacity: 0.8; } </style> </head> <body> <!-- Header Navbar --> <nav class="navbar navbar-expand-lg navbar-light admin-navbar sticky-top"> <div class="container"> <a class="navbar-brand admin-brand" href="admin.php"> <i class="fa-solid fa-user-shield me-2"></i> REDSHOP Admin Console </a> <div class="ms-auto d-flex align-items-center"> <a href="index.php" class="btn btn-outline-secondary btn-sm me-2" target="_blank"><i class="fa-solid fa-store me-1"></i> ไปหน้าหลักของร้าน</a> <a href="admin.php?logout=1" class="btn btn-danger btn-sm"><i class="fa-solid fa-right-from-bracket"></i> ออกจากระบบ</a> </div> </div> </nav> <!-- Main Container --> <div class="container my-4"> <!-- แจ้งเตือนสถานะการกระทำ --> <?php if (isset($_SESSION['admin_success'])): ?> <div class="alert alert-success alert-dismissible fade show alert-preorder mb-4" role="alert"> <i class="fa-solid fa-circle-check me-2"></i> <?php echo $_SESSION['admin_success']; unset($_SESSION['admin_success']); ?> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> <?php endif; ?> <?php if (isset($_SESSION['admin_error'])): ?> <div class="alert alert-danger alert-dismissible fade show alert-preorder mb-4" role="alert"> <i class="fa-solid fa-circle-exclamation me-2"></i> <?php echo $_SESSION['admin_error']; unset($_SESSION['admin_error']); ?> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> </div> <?php endif; ?> <?php if (isset($_SESSION['admin_bulk_errors'])): ?> <div class="alert alert-warning alert-preorder mb-4" role="alert"> <h6><i class="fa-solid fa-triangle-exclamation me-2"></i> พบปัญหาบางรายการขณะอัปเดตไฟล์ CSV:</h6> <ul class="mb-0 small"> <?php foreach ($_SESSION['admin_bulk_errors'] as $err): ?> <li><?php echo $err; ?></li> <?php endforeach; ?> </ul> </div> <?php unset($_SESSION['admin_bulk_errors']); ?> <?php endif; ?> <div class="row g-4"> <!-- Sidebar Navigation --> <div class="col-12 col-lg-3"> <div class="admin-sidebar"> <h5 class="mb-3 px-2 text-muted font-weight-bold text-uppercase fs-7">เมนูควบคุม</h5> <div class="nav flex-column nav-pills" id="adminTabs" role="tablist"> <a href="admin.php?tab=dashboard" class="nav-link <?php echo $active_tab === 'dashboard' ? 'active' : ''; ?>"> <i class="fa-solid fa-chart-pie me-2"></i> แดชบอร์ด & ยอดส่งโรงงาน </a> <a href="admin.php?tab=products" class="nav-link <?php echo $active_tab === 'products' ? 'active' : ''; ?>"> <i class="fa-solid fa-boxes-stacked me-2"></i> จัดการข้อมูลสินค้า </a> <a href="admin.php?tab=orders" class="nav-link <?php echo $active_tab === 'orders' ? 'active' : ''; ?>"> <i class="fa-solid fa-file-invoice-dollar me-2"></i> จัดการยอดจอง & สลิป <?php if ($stat_pending_slips > 0): ?> <span class="badge bg-danger rounded-pill float-end"><?php echo $stat_pending_slips; ?></span> <?php endif; ?> </a> </div> </div> </div> <!-- Content Area --> <div class="col-12 col-lg-9"> <!-- TAB 1: DASHBOARD & REPORTS --> <?php if ($active_tab === 'dashboard'): ?> <div> <h4 class="mb-4 font-weight-bold text-dark"><i class="fa-solid fa-chart-pie text-danger me-2"></i> แดชบอร์ด & รายงานสรุป</h4> <!-- Cards Metrics --> <div class="row g-3 mb-4"> <div class="col-12 col-sm-6 col-md-3"> <div class="card stat-card p-3"> <div class="d-flex align-items-center justify-content-between"> <div> <span class="text-muted small">ยอดขายรวมที่อนุมัติ</span> <h4 class="mb-0 text-danger font-weight-bold mt-1">฿<?php echo number_format($stat_revenue, 2); ?></h4> </div> <div class="bg-light-red p-2 rounded text-danger fs-3"> <i class="fa-solid fa-wallet"></i> </div> </div> </div> </div> <div class="col-12 col-sm-6 col-md-3"> <div class="card stat-card p-3"> <div class="d-flex align-items-center justify-content-between"> <div> <span class="text-muted small">จำนวนการจองทั้งหมด</span> <h4 class="mb-0 text-dark font-weight-bold mt-1"><?php echo $stat_total_bookings; ?> ครั้ง</h4> </div> <div class="bg-light p-2 rounded text-secondary fs-3"> <i class="fa-solid fa-cart-arrow-down"></i> </div> </div> </div> </div> <div class="col-12 col-sm-6 col-md-3"> <div class="card stat-card p-3"> <div class="d-flex align-items-center justify-content-between"> <div> <span class="text-muted small">สลิปรอตรวจสอบ</span> <h4 class="mb-0 text-warning font-weight-bold mt-1"><?php echo $stat_pending_slips; ?> สลิป</h4> </div> <div class="bg-warning-subtle p-2 rounded text-warning fs-3"> <i class="fa-solid fa-hourglass-half"></i> </div> </div> </div> </div> <div class="col-12 col-sm-6 col-md-3"> <div class="card stat-card p-3"> <div class="d-flex align-items-center justify-content-between"> <div> <span class="text-muted small">ยอดจองอนุมัติแล้ว</span> <h4 class="mb-0 text-success font-weight-bold mt-1"><?php echo $stat_approved_bookings; ?> สิทธิ์</h4> </div> <div class="bg-success-subtle p-2 rounded text-success fs-3"> <i class="fa-solid fa-circle-check"></i> </div> </div> </div> </div> </div> <!-- Factory Order Report Table --> <div class="card border-0 shadow-sm mb-4"> <div class="card-header bg-white border-bottom py-3"> <h5 class="card-title mb-0 text-dark font-weight-bold"> <i class="fa-solid fa-industry text-danger me-2"></i> สรุปยอดจองสำหรับส่งผลิตที่โรงงาน (คัดกรองเฉพาะสลิปที่อนุมัติแล้ว) </h5> </div> <div class="card-body"> <?php if (empty($factory_orders)): ?> <p class="text-center text-muted py-3">ไม่มีข้อมูลการจองที่อนุมัติการชำระเงินแล้วสำหรับการส่งยอดผลิต</p> <?php else: ?> <div class="table-responsive"> <table class="table table-striped table-hover align-middle"> <thead class="table-light"> <tr> <th>ชื่อสินค้า</th> <th>ตัวเลือกย่อย / ไซส์ / สี</th> <th class="text-center">จำนวนคำสั่งจอง</th> <th class="text-center bg-danger-subtle text-danger font-weight-bold">รวมยอดสั่งผลิต (ชิ้น)</th> </tr> </thead> <tbody> <?php foreach ($factory_orders as $fo): ?> <tr> <td><strong><?php echo htmlspecialchars($fo['product_name']); ?></strong></td> <td><span class="badge bg-secondary"><?php echo htmlspecialchars($fo['variant_name']); ?></span></td> <td class="text-center"><?php echo $fo['total_orders']; ?></td> <td class="text-center font-weight-bold text-danger bg-light-red fs-5"><?php echo $fo['total_qty']; ?></td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> </div> <!-- Product Revenues Breakdown --> <div class="row"> <div class="col-12 col-md-6"> <div class="card border-0 shadow-sm"> <div class="card-header bg-white border-bottom py-3"> <h6 class="card-title mb-0 font-weight-bold"><i class="fa-solid fa-sack-dollar text-success me-2"></i> รายได้รวมแยกตามสินค้า (ที่อนุมัติแล้ว)</h6> </div> <div class="card-body"> <?php if (empty($product_revenues)): ?> <p class="text-center text-muted small py-2">ยังไม่มีรายได้ที่อนุมัติ</p> <?php else: ?> <ul class="list-group list-group-flush"> <?php foreach ($product_revenues as $pr): ?> <li class="list-group-item d-flex justify-content-between align-items-center"> <span><?php echo htmlspecialchars($pr['name']); ?></span> <span class="font-weight-bold text-success">฿<?php echo number_format($pr['revenue'], 2); ?></span> </li> <?php endforeach; ?> </ul> <?php endif; ?> </div> </div> </div> <div class="col-12 col-md-6"> <div class="card border-0 shadow-sm"> <div class="card-header bg-white border-bottom py-3"> <h6 class="card-title mb-0 font-weight-bold"><i class="fa-solid fa-credit-card text-primary me-2"></i> สถิติช่องทางการชำระเงิน</h6> </div> <div class="card-body"> <div class="d-flex justify-content-between align-items-center py-2 border-bottom"> <span>โอนเงินผ่านบัญชีธนาคาร (Bank Transfer)</span> <span class="badge bg-primary rounded-pill"><?php echo $stat_total_bookings; ?> รายการ</span> </div> <div class="d-flex justify-content-between align-items-center py-2"> <span>ช่องทางอื่นๆ</span> <span class="text-muted">0 รายการ</span> </div> </div> </div> </div> </div> </div> <?php endif; ?> <!-- TAB 2: PRODUCT MANAGEMENT --> <?php if ($active_tab === 'products'): ?> <div> <div class="d-flex justify-content-between align-items-center mb-3"> <h4 class="font-weight-bold text-dark mb-0"><i class="fa-solid fa-boxes-stacked text-danger me-2"></i> จัดการข้อมูลสินค้า</h4> <div class="d-flex gap-2"> <button class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#manageCategoryModal"> <i class="fa-solid fa-tags me-1"></i> จัดการชื่อหมวดหมู่ </button> <button class="btn btn-red btn-sm" data-bs-toggle="modal" data-bs-target="#addProductModal"> <i class="fa-solid fa-plus-circle me-1"></i> เพิ่มสินค้าใหม่ </button> </div> </div> <!-- Category Filter Tabs (หลังบ้าน) --> <div class="card border-0 shadow-sm mb-3"> <div class="card-body p-2 d-flex flex-wrap gap-2 align-items-center"> <span class="text-muted small fw-bold ms-2 me-1"><i class="fa-solid fa-filter me-1"></i> กรองตามหมวดหมู่:</span> <button type="button" class="btn btn-sm btn-outline-danger active admin-category-btn" onclick="filterAdminCategory('all', this)"> ทั้งหมด (<?php echo count($products_list); ?>) </button> <?php foreach ($existing_categories as $cat): ?> <?php $cnt = 0; foreach ($products_list as $p) { if (($p['category'] ?? 'ทั่วไป') === $cat) $cnt++; } ?> <button type="button" class="btn btn-sm btn-outline-secondary admin-category-btn" onclick="filterAdminCategory('<?php echo htmlspecialchars($cat); ?>', this)"> <?php echo htmlspecialchars($cat); ?> (<?php echo $cnt; ?>) </button> <?php endforeach; ?> </div> </div> <!-- Product List Table --> <div class="card border-0 shadow-sm"> <div class="card-body p-0"> <div class="table-responsive"> <table class="table table-hover align-middle mb-0" style="table-layout: fixed;"> <colgroup> <col style="width: 70px;"> <col style="width: 260px;"> <col style="width: 110px;"> <col style="width: 160px;"> <col style="width: 150px;"> <col style="width: 160px;"> </colgroup> <thead class="table-light"> <tr> <th>รูป</th> <th>ชื่อสินค้า / หมวดหมู่</th> <th>ราคาต่อชิ้น</th> <th>ช่วงเวลารับจอง</th> <th>ตัวเลือก (Variants)</th> <th class="text-end">การจัดการ</th> </tr> </thead> <tbody> <?php if (empty($products_list)): ?> <tr> <td colspan="6" class="text-center text-muted py-4">ไม่พบข้อมูลสินค้า</td> </tr> <?php else: ?> <?php foreach ($products_list as $product): ?> <tr class="admin-product-row" data-category="<?php echo htmlspecialchars($product['category'] ?? 'ทั่วไป'); ?>" style="height: 80px;"> <td style="width:70px; padding: 8px;"> <?php $prod_img = !empty($product['image_url']) ? htmlspecialchars($product['image_url']) : 'assets/images/default.png'; ?> <img src="<?php echo $prod_img; ?>" onerror="this.onerror=null; this.src='assets/images/default.png';" style="width:56px; height:56px; object-fit:cover; border-radius:8px; border:1px solid #edf2f7; background-color:#f8f9fa;" alt="<?php echo htmlspecialchars($product['name']); ?>"> </td> <td style="max-width:260px;"> <div class="d-flex align-items-start gap-1 flex-wrap mb-1"> <strong class="text-truncate d-block" style="max-width:180px;"><?php echo htmlspecialchars($product['name']); ?></strong> <span class="badge bg-danger-subtle text-danger border border-danger-subtle px-2 py-1 rounded-pill" style="font-size:0.7rem; white-space:nowrap;"> <i class="fa-solid fa-layer-group me-1"></i><?php echo htmlspecialchars($product['category'] ?? 'ทั่วไป'); ?> </span> </div> <div class="small text-muted" style="overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; max-width:240px;"> <?php echo htmlspecialchars($product['description']); ?> </div> </td> <td class="fw-bold text-danger" style="white-space:nowrap;">฿<?php echo number_format($product['price'], 2); ?></td> <td> <div class="small"> <strong>เริ่ม:</strong> <?php echo $product['start_time'] ? date('d/m/Y H:i', strtotime($product['start_time'])) : 'ไม่ระบุ'; ?><br> <strong>สิ้นสุด:</strong> <?php echo $product['end_time'] ? date('d/m/Y H:i', strtotime($product['end_time'])) : 'ไม่ระบุ'; ?> </div> </td> <td> <button class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#variantsModal_<?php echo $product['id']; ?>" style="white-space:nowrap;"> <i class="fa-solid fa-tags"></i> จัดการตัวเลือก (<?php echo isset($variants_list[$product['id']]) ? count($variants_list[$product['id']]) : 0; ?>) </button> </td> <td class="text-end" style="white-space:nowrap;"> <button class="btn btn-outline-primary btn-sm me-1" data-bs-toggle="modal" data-bs-target="#editProductModal" data-id="<?php echo $product['id']; ?>" data-name="<?php echo htmlspecialchars($product['name']); ?>" data-category="<?php echo htmlspecialchars($product['category'] ?? 'ทั่วไป'); ?>" data-desc="<?php echo htmlspecialchars($product['description']); ?>" data-price="<?php echo $product['price']; ?>" data-video="<?php echo htmlspecialchars($product['video_url']); ?>" data-image="<?php echo htmlspecialchars($product['image_url'] ?? ''); ?>" data-start="<?php echo $product['start_time'] ? date('Y-m-d\TH:i', strtotime($product['start_time'])) : ''; ?>" data-end="<?php echo $product['end_time'] ? date('Y-m-d\TH:i', strtotime($product['end_time'])) : ''; ?>"> <i class="fa-solid fa-pencil"></i> แก้ไข </button> <a href="admin.php?action=delete_product&id=<?php echo $product['id']; ?>" class="btn btn-outline-danger btn-sm" onclick="return confirm('คุณต้องการลบสินค้านี้ใช่หรือไม่? ระบบจะลบตัวเลือกและการจองของสินค้าชิ้นนี้ทั้งหมดทันที')"> <i class="fa-solid fa-trash"></i> ลบ </a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </div> </div> </div> <!-- Variant Modals (moved outside the table for clean DOM) --> <?php foreach ($products_list as $product): ?> <div class="modal fade" id="variantsModal_<?php echo $product['id']; ?>" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h6 class="modal-title fw-bold text-dark"> <i class="fa-solid fa-tags text-danger me-2"></i>ตัวเลือกของ: <?php echo htmlspecialchars($product['name']); ?> </h6> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <table class="table table-bordered table-sm align-middle mb-0"> <thead class="table-light"> <tr> <th>ชื่อตัวเลือก (ไซส์/สี)</th> <th class="text-center">สต็อกคงเหลือ</th> <th class="text-center">Made-to-Order</th> <th class="text-center">การจัดการ</th> </tr> </thead> <tbody> <?php if (empty($variants_list[$product['id']])): ?> <tr><td colspan="4" class="text-center text-muted py-2">ยังไม่มีตัวเลือกสินค้า</td></tr> <?php else: ?> <?php foreach ($variants_list[$product['id']] as $v): ?> <tr> <td><?php echo htmlspecialchars($v['variant_name']); ?></td> <td class="text-center"><?php echo (int)$v['is_unlimited'] === 1 ? '-' : $v['stock']; ?></td> <td class="text-center"> <?php if ((int)$v['is_unlimited'] === 1): ?> <span class="badge bg-success">เปิดใช้งาน</span> <?php else: ?> <span class="text-muted small">จำกัดสต็อก</span> <?php endif; ?> </td> <td class="text-center"> <a href="admin.php?action=delete_variant&id=<?php echo $v['id']; ?>" class="btn btn-sm btn-outline-danger py-0 px-2" onclick="return confirm('ลบตัวเลือกนี้ใช่หรือไม่?')"> <i class="fa-solid fa-xmark"></i> ลบ </a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> <!-- Add Variant Form --> <h6 class="mt-4 border-top pt-3 fw-bold text-dark">เพิ่มตัวเลือกย่อยใหม่</h6> <form action="admin.php?action=add_variant" method="POST" class="row g-2 mt-1"> <input type="hidden" name="product_id" value="<?php echo $product['id']; ?>"> <div class="col-12 col-sm-6"> <input type="text" name="variant_name" class="form-control" placeholder="เช่น สีแดงสด / Size L" required> </div> <div class="col-6 col-sm-3"> <input type="number" name="stock" class="form-control" placeholder="สต็อก" min="0" value="10"> </div> <div class="col-6 col-sm-3 d-flex align-items-center"> <div class="form-check"> <input class="form-check-input" type="checkbox" name="is_unlimited" id="is_unlimited_<?php echo $product['id']; ?>" value="1"> <label class="form-check-label small" for="is_unlimited_<?php echo $product['id']; ?>">Made-to-Order</label> </div> </div> <div class="col-12 mt-2"> <button type="submit" class="btn btn-red w-100 btn-sm"><i class="fa-solid fa-plus"></i> เพิ่มตัวเลือก</button> </div> </form> </div> </div> </div> </div> <?php endforeach; ?> </div> <?php endif; ?> <!-- TAB 3: ORDER MANAGEMENT --> <?php if ($active_tab === 'orders'): ?> <div> <div class="d-flex justify-content-between align-items-center mb-4"> <h4 class="font-weight-bold text-dark mb-0"><i class="fa-solid fa-file-invoice-dollar text-danger me-2"></i> จัดการยอดจอง & อัปเดตพัสดุ</h4> <!-- Bulk Upload Section Trigger --> <button class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#bulkTrackingModal"> <i class="fa-solid fa-file-csv me-1"></i> อัปโหลดเลขพัสดุแบบกลุ่ม (Bulk Upload) </button> </div> <!-- Orders List Table --> <div class="card border-0 shadow-sm"> <div class="card-body p-0"> <div class="table-responsive"> <table class="table table-hover align-middle mb-0"> <thead class="table-light"> <tr> <th class="text-center">ID การจอง</th> <th>ข้อมูลลูกค้า</th> <th>สินค้าที่จอง</th> <th>ราคารวม</th> <th class="text-center">ช่องทางชำระ</th> <th class="text-center">สลิปเงินโอน</th> <th class="text-center">สถานะสลิป</th> <th>เลขพัสดุ (Tracking)</th> <th class="text-end">การจัดการ</th> </tr> </thead> <tbody> <?php if (empty($bookings_list)): ?> <tr> <td colspan="8" class="text-center text-muted py-4">ไม่พบข้อมูลคำสั่งจองใดๆ</td> </tr> <?php else: ?> <?php foreach ($bookings_list as $booking): ?> <tr> <td class="text-center"><strong>#<?php echo $booking['id']; ?></strong></td> <td> <div><strong><?php echo htmlspecialchars($booking['customer_name']); ?></strong></div> <div class="small text-muted"><i class="fa-solid fa-phone"></i> <?php echo htmlspecialchars($booking['customer_phone']); ?></div> </td> <td> <div><strong><?php echo htmlspecialchars($booking['product_name']); ?></strong></div> <div><span class="badge bg-light text-dark border"><?php echo htmlspecialchars($booking['variant_name']); ?></span> <span class="small text-muted">x<?php echo $booking['quantity']; ?> ชิ้น</span></div> </td> <td class="text-danger font-weight-bold">฿<?php echo number_format($booking['total_price'], 2); ?></td> <!-- ช่องทางชำระเงิน --> <td class="text-center"> <?php $pm = isset($booking['payment_method']) ? $booking['payment_method'] : 'Bank Transfer'; if ($pm === 'PromptPay') { echo '<span class="badge" style="background:#FF6B35; color:white;"><i class="fa-solid fa-qrcode me-1"></i>PromptPay</span>'; } else { echo '<span class="badge bg-info text-dark"><i class="fa-solid fa-building-columns me-1"></i>โอนธนาคาร</span>'; } ?> </td> <td class="text-center"> <?php if (!empty($booking['slip_image_url']) && file_exists($booking['slip_image_url'])): ?> <img src="<?php echo htmlspecialchars($booking['slip_image_url']); ?>" class="slip-thumbnail shadow-sm" alt="Slip Preview" data-bs-toggle="modal" data-bs-target="#viewSlipModal" data-src="<?php echo htmlspecialchars($booking['slip_image_url']); ?>"> <?php else: ?> <span class="text-muted small">ไม่มีไฟล์สลิป</span> <?php endif; ?> </td> <td class="text-center"> <?php $p_status = $booking['payment_status']; $badge_class = 'status-badge-pending'; $status_th = 'รอตรวจสอบ'; if ($p_status === 'Approved') { $badge_class = 'status-badge-approved'; $status_th = 'อนุมัติแล้ว'; } elseif ($p_status === 'Rejected') { $badge_class = 'status-badge-rejected'; $status_th = 'ปฏิเสธ/สลิปไม่ผ่าน'; } ?> <span class="badge <?php echo $badge_class; ?> px-2 py-1"><?php echo $status_th; ?></span> </td> <td> <form action="admin.php?action=update_tracking" method="POST" class="d-flex"> <input type="hidden" name="booking_id" value="<?php echo $booking['id']; ?>"> <input type="text" name="tracking_number" class="form-control form-control-sm me-1" value="<?php echo htmlspecialchars($booking['tracking_number'] ?? ''); ?>" placeholder="เช่น TH0102AB" style="width: 110px;"> <button type="submit" class="btn btn-outline-secondary btn-sm px-2 py-0"><i class="fa-solid fa-save"></i></button> </form> </td> <td class="text-end"> <?php if ($p_status === 'Pending'): ?> <a href="admin.php?action=approve_booking&id=<?php echo $booking['id']; ?>" class="btn btn-success btn-sm me-1 px-2 py-1" onclick="return confirm('ยืนยันอนุมัติสลิปการโอนเงินชิ้นนี้ ใช่หรือไม่?')"> <i class="fa-solid fa-check"></i> อนุมัติ </a> <a href="admin.php?action=reject_booking&id=<?php echo $booking['id']; ?>" class="btn btn-danger btn-sm px-2 py-1" onclick="return confirm('คุณต้องการปฏิเสธสลิปชิ้นนี้ใช่หรือไม่? ระบบจะเพิ่มยอดสต็อกคืนตารางตัวเลือกอัตโนมัติ')"> <i class="fa-solid fa-times"></i> ปฏิเสธ </a> <?php elseif ($p_status === 'Approved'): ?> <a href="admin.php?action=reject_booking&id=<?php echo $booking['id']; ?>" class="btn btn-outline-danger btn-sm px-2 py-1" onclick="return confirm('ยกเลิกการอนุมัติและปรับสถานะเป็นปฏิเสธใช่หรือไม่? ระบบจะนำสต็อกกลับเข้าคลัง')"> <i class="fa-solid fa-rotate-left"></i> ยกเลิกอนุมัติ </a> <?php else: ?> <span class="text-muted small">-</span> <?php endif; ?> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </div> </div> </div> </div> <?php endif; ?> </div> </div> </div> <!-- MODAL: Add Product --> <div class="modal fade" id="addProductModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title font-weight-bold text-dark"><i class="fa-solid fa-circle-plus text-danger me-2"></i>เพิ่มข้อมูลสินค้าใหม่</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form action="admin.php?action=add_product" method="POST" enctype="multipart/form-data" onsubmit="return beforeSubmitAddProduct(this)"> <div class="modal-body"> <div class="mb-3"> <label for="add_name" class="form-label">ชื่อสินค้า <span class="text-danger">*</span></label> <input type="text" class="form-control" id="add_name" name="name" required placeholder="เช่น เสื้อยืดที่ระลึก CMTC 2026"> </div> <div class="mb-3"> <label for="add_category" class="form-label">หมวดหมู่สินค้า <span class="text-danger">*</span></label> <select class="form-select" id="add_category" name="category" required onchange="handleAddCategoryChange(this)"> <option value="">— เลือกหมวดหมู่ —</option> <?php foreach ($existing_categories as $cat): ?> <option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option> <?php endforeach; ?> <?php $default_cats = ['เสื้อผ้าและเครื่องแต่งกาย', 'กระเป๋าและอุปกรณ์', 'ของสะสมและของที่ระลึก', 'เครื่องเขียนและอุปกรณ์การเรียน', 'อาหารและเครื่องดื่ม', 'อิเล็กทรอนิกส์และอุปกรณ์', 'ทั่วไป']; foreach ($default_cats as $dc) { if (!in_array($dc, $existing_categories)) { echo '<option value="' . htmlspecialchars($dc) . '">' . htmlspecialchars($dc) . '</option>'; } } ?> <option value="__new__">➕ พิมพ์หมวดหมู่ใหม่...</option> </select> <!-- ช่องพิมพ์หมวดใหม่ (ซ่อน) --> <input type="text" class="form-control mt-2 d-none" id="add_category_custom" placeholder="พิมพ์ชื่อหมวดหมู่ใหม่ เช่น สุขภาพและความงาม"> </div> <div class="mb-3"> <label for="add_desc" class="form-label">รายละเอียดสินค้า <span class="text-danger">*</span></label> <textarea class="form-control" id="add_desc" name="description" rows="3" required placeholder="ข้อมูลสินค้าและการจัดส่ง"></textarea> </div> <div class="mb-3"> <label for="add_price" class="form-label">ราคาสินค้าต่อชิ้น (บาท) <span class="text-danger">*</span></label> <input type="number" class="form-control" id="add_price" name="price" step="0.01" min="0" required placeholder="เช่น 390"> </div> <div class="mb-3"> <label for="add_video" class="form-label">ลิงก์วิดีโอรีวิว (Embedded URL)</label> <input type="url" class="form-control" id="add_video" name="video_url" placeholder="เช่น https://www.youtube.com/embed/dQw4w9WgXcQ"> <div class="form-text">ระบุลิงก์ YouTube แบบ Embed หรือลิงก์ไฟล์ mp4 โดยตรง</div> </div> <div class="row"> <div class="col-6 mb-3"> <label for="add_start" class="form-label">เวลาเปิดรับจอง</label> <input type="datetime-local" class="form-control" id="add_start" name="start_time"> </div> <div class="col-6 mb-3"> <label for="add_end" class="form-label">เวลาปิดรับจอง</label> <input type="datetime-local" class="form-control" id="add_end" name="end_time"> </div> </div> <div class="mb-3"> <label for="add_image_url" class="form-label">ลิงก์รูปภาพสินค้า (Image URL)</label> <input type="url" class="form-control" id="add_image_url" name="image_url" placeholder="เช่น https://example.com/images/product.jpg"> <div class="form-text">ระบุลิงก์ URL ของรูปภาพโดยตรง หรือเลือกอัปโหลดไฟล์ด้านล่าง</div> </div> <div class="mb-3"> <label for="add_image" class="form-label">อัปโหลดรูปภาพสินค้าจากเครื่อง</label> <input type="file" class="form-control" id="add_image" name="image" accept="image/png, image/jpeg, image/webp, image/gif"> </div> </div> <div class="modal-footer border-top-0"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">ยกเลิก</button> <button type="submit" class="btn btn-red"><i class="fa-solid fa-circle-check me-1"></i> บันทึกข้อมูล</button> </div> </form> </div> </div> </div> <!-- MODAL: Edit Product --> <div class="modal fade" id="editProductModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title font-weight-bold text-dark"><i class="fa-solid fa-pencil text-danger me-2"></i>แก้ไขรายละเอียดสินค้า</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form action="admin.php?action=edit_product" method="POST" enctype="multipart/form-data" onsubmit="return beforeSubmitEditProduct(this)"> <input type="hidden" name="id" id="edit_id"> <div class="modal-body"> <div class="mb-3"> <label for="edit_name" class="form-label">ชื่อสินค้า <span class="text-danger">*</span></label> <input type="text" class="form-control" id="edit_name" name="name" required> </div> <div class="mb-3"> <label for="edit_category" class="form-label">หมวดหมู่สินค้า <span class="text-danger">*</span></label> <select class="form-select" id="edit_category" name="category" required onchange="handleEditCategoryChange(this)"> <option value="">— เลือกหมวดหมู่ —</option> <?php foreach ($existing_categories as $cat): ?> <option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option> <?php endforeach; ?> <?php $default_cats2 = ['เสื้อผ้าและเครื่องแต่งกาย', 'กระเป๋าและอุปกรณ์', 'ของสะสมและของที่ระลึก', 'เครื่องเขียนและอุปกรณ์การเรียน', 'อาหารและเครื่องดื่ม', 'อิเล็กทรอนิกส์และอุปกรณ์', 'ทั่วไป']; foreach ($default_cats2 as $dc) { if (!in_array($dc, $existing_categories)) { echo '<option value="' . htmlspecialchars($dc) . '">' . htmlspecialchars($dc) . '</option>'; } } ?> <option value="__new__">➕ พิมพ์หมวดหมู่ใหม่...</option> </select> <!-- ช่องพิมพ์หมวดใหม่ (ซ่อน) --> <input type="text" class="form-control mt-2 d-none" id="edit_category_custom" placeholder="พิมพ์ชื่อหมวดหมู่ใหม่"> </div> <div class="mb-3"> <label for="edit_desc" class="form-label">รายละเอียดสินค้า <span class="text-danger">*</span></label> <textarea class="form-control" id="edit_desc" name="description" rows="3" required></textarea> </div> <div class="mb-3"> <label for="edit_price" class="form-label">ราคาสินค้าต่อชิ้น (บาท) <span class="text-danger">*</span></label> <input type="number" class="form-control" id="edit_price" name="price" step="0.01" min="0" required> </div> <div class="mb-3"> <label for="edit_video" class="form-label">ลิงก์วิดีโอรีวิว (Embedded URL)</label> <input type="url" class="form-control" id="edit_video" name="video_url"> </div> <div class="row"> <div class="col-6 mb-3"> <label for="edit_start" class="form-label">เวลาเปิดรับจอง</label> <input type="datetime-local" class="form-control" id="edit_start" name="start_time"> </div> <div class="col-6 mb-3"> <label for="edit_end" class="form-label">เวลาปิดรับจอง</label> <input type="datetime-local" class="form-control" id="edit_end" name="end_time"> </div> </div> <div class="mb-3"> <label for="edit_image_url" class="form-label">ลิงก์รูปภาพสินค้า (Image URL)</label> <input type="text" class="form-control" id="edit_image_url" name="image_url" placeholder="เช่น https://example.com/images/product.jpg"> <div class="form-text">สามารถแก้ไข URL รูปภาพได้โดยตรง หรืออัปโหลดไฟล์ใหม่ด้านล่าง</div> </div> <div class="mb-3"> <label for="edit_image" class="form-label">เปลี่ยนรูปภาพสินค้าโดยอัปโหลดไฟล์ (เว้นว่างไว้หากไม่ต้องการเปลี่ยน)</label> <input type="file" class="form-control" id="edit_image" name="image" accept="image/png, image/jpeg, image/webp, image/gif"> </div> </div> <div class="modal-footer border-top-0"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">ยกเลิก</button> <button type="submit" class="btn btn-red"><i class="fa-solid fa-circle-check me-1"></i> บันทึกการแก้ไข</button> </div> </form> </div> </div> </div> <!-- MODAL: Manage / Rename Category --> <div class="modal fade" id="manageCategoryModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title font-weight-bold text-dark"><i class="fa-solid fa-tags text-danger me-2"></i>จัดการและเปลี่ยนชื่อหมวดหมู่</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form action="admin.php?action=rename_category" method="POST"> <div class="modal-body"> <div class="alert alert-info py-2 small"> <i class="fa-solid fa-circle-info me-1"></i> เมื่อเปลี่ยนชื่อหมวดหมู่ สินค้าทั้งหมดที่อยู่ในหมวดหมู่เดิมจะถูกอัปเดตเป็นชื่อหมวดหมู่ใหม่อัตโนมัติ </div> <div class="mb-3"> <label for="old_category" class="form-label">เลือกหมวดหมู่ที่ต้องการเปลี่ยนชื่อ <span class="text-danger">*</span></label> <select class="form-select" id="old_category" name="old_category" required> <option value="" disabled selected>-- เลือกหมวดหมู่เดิม --</option> <?php foreach ($existing_categories as $cat): ?> <option value="<?php echo htmlspecialchars($cat); ?>"><?php echo htmlspecialchars($cat); ?></option> <?php endforeach; ?> </select> </div> <div class="mb-3"> <label for="new_category" class="form-label">ชื่อหมวดหมู่ใหม่ <span class="text-danger">*</span></label> <input type="text" class="form-control" id="new_category" name="new_category" required placeholder="เช่น เสื้อผ้าแฟชั่น, อุปกรณ์เสริม"> </div> </div> <div class="modal-footer border-top-0"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">ยกเลิก</button> <button type="submit" class="btn btn-red"><i class="fa-solid fa-save me-1"></i> ยืนยันเปลี่ยนชื่อ</button> </div> </form> </div> </div> </div> <!-- MODAL: View Slip Fullsize --> <div class="modal fade" id="viewSlipModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered modal-md"> <div class="modal-content"> <div class="modal-header border-bottom-0"> <h6 class="modal-title font-weight-bold text-dark"><i class="fa-solid fa-image me-1"></i> หลักฐานสลิปการโอนเงิน</h6> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body text-center p-0"> <img src="" id="slip_full_image" class="img-fluid rounded-bottom" alt="Slip Full View" style="max-height: 80vh;"> </div> </div> </div> </div> <!-- MODAL: Bulk Tracking Upload --> <div class="modal fade" id="bulkTrackingModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title font-weight-bold text-dark"><i class="fa-solid fa-file-csv text-danger me-2"></i>อัปโหลดเลขพัสดุกลุ่ม (CSV)</h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <form action="admin.php?action=bulk_tracking" method="POST" enctype="multipart/form-data"> <div class="modal-body"> <div class="alert alert-info py-2" style="font-size: 0.85rem;"> <strong>รูปแบบไฟล์ CSV ที่ถูกต้อง:</strong><br> บรรทัดแรก: หัวตาราง (เช่น booking_id, tracking_number)<br> บรรทัดถัดไป: <code>12,TH01929348AB</code><br> * กรุณาบันทึกไฟล์เป็น UTF-8 เพื่อป้องกันการแสดงผลเพี้ยน </div> <div class="mb-3"> <label for="csv_file" class="form-label">เลือกไฟล์ CSV <span class="text-danger">*</span></label> <input type="file" class="form-control" id="csv_file" name="csv_file" accept=".csv" required> </div> </div> <div class="modal-footer border-top-0"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">ยกเลิก</button> <button type="submit" class="btn btn-red"><i class="fa-solid fa-upload me-1"></i> เริ่มอัปเดตข้อมูล</button> </div> </form> </div> </div> </div> <!-- Bootstrap 5 Bundle JS --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script> // ========================================== // Category Dropdown Handlers // ========================================== /** เมื่อเลือก "➕ พิมพ์หมวดหมู่ใหม่..." ใน Add Modal */ function handleAddCategoryChange(selectEl) { const customInput = document.getElementById('add_category_custom'); if (selectEl.value === '__new__') { customInput.classList.remove('d-none'); customInput.required = true; customInput.focus(); } else { customInput.classList.add('d-none'); customInput.required = false; customInput.value = ''; } } /** เมื่อเลือก "➕ พิมพ์หมวดหมู่ใหม่..." ใน Edit Modal */ function handleEditCategoryChange(selectEl) { const customInput = document.getElementById('edit_category_custom'); if (selectEl.value === '__new__') { customInput.classList.remove('d-none'); customInput.required = true; customInput.focus(); } else { customInput.classList.add('d-none'); customInput.required = false; customInput.value = ''; } } /** ก่อน submit ฟอร์ม Add Product — แทนค่า category จาก custom input ถ้าเลือก __new__ */ function beforeSubmitAddProduct(form) { const selectEl = document.getElementById('add_category'); const customInput = document.getElementById('add_category_custom'); if (selectEl.value === '__new__') { const customVal = customInput.value.trim(); if (!customVal) { customInput.focus(); customInput.classList.add('is-invalid'); return false; // ห้าม submit } // สร้าง hidden input แทน let hidden = form.querySelector('input[name="category"]'); if (!hidden) { hidden = document.createElement('input'); hidden.type = 'hidden'; hidden.name = 'category'; form.appendChild(hidden); } hidden.value = customVal; selectEl.removeAttribute('name'); // ป้องกัน __new__ ถูกส่ง } return true; } /** ก่อน submit ฟอร์ม Edit Product — แทนค่า category จาก custom input ถ้าเลือก __new__ */ function beforeSubmitEditProduct(form) { const selectEl = document.getElementById('edit_category'); const customInput = document.getElementById('edit_category_custom'); if (selectEl.value === '__new__') { const customVal = customInput.value.trim(); if (!customVal) { customInput.focus(); customInput.classList.add('is-invalid'); return false; } let hidden = form.querySelector('input[name="category"]'); if (!hidden) { hidden = document.createElement('input'); hidden.type = 'hidden'; hidden.name = 'category'; form.appendChild(hidden); } hidden.value = customVal; selectEl.removeAttribute('name'); } return true; } // ========================================== // Edit Product Modal — Populate Fields // ========================================== const editProductModal = document.getElementById('editProductModal'); if (editProductModal) { editProductModal.addEventListener('show.bs.modal', function (event) { const button = event.relatedTarget; document.getElementById('edit_id').value = button.getAttribute('data-id'); document.getElementById('edit_name').value = button.getAttribute('data-name'); document.getElementById('edit_desc').value = button.getAttribute('data-desc'); document.getElementById('edit_price').value = button.getAttribute('data-price'); document.getElementById('edit_video').value = button.getAttribute('data-video'); document.getElementById('edit_image_url').value = button.getAttribute('data-image') || ''; document.getElementById('edit_start').value = button.getAttribute('data-start'); document.getElementById('edit_end').value = button.getAttribute('data-end'); // ตั้งค่า category dropdown const dataCat = button.getAttribute('data-category') || 'ทั่วไป'; const editCatSelect = document.getElementById('edit_category'); const editCatCustom = document.getElementById('edit_category_custom'); // รีเซ็ตสถานะ editCatCustom.classList.add('d-none'); editCatCustom.required = false; editCatCustom.value = ''; editCatSelect.name = 'category'; // คืนชื่อ attribute ถ้าถูกลบไป // ตรวจสอบว่าหมวดหมู่นั้นมีใน dropdown หรือไม่ let found = false; for (let opt of editCatSelect.options) { if (opt.value === dataCat) { found = true; break; } } if (found) { editCatSelect.value = dataCat; } else { // หมวดหมู่ไม่มีใน list ให้เพิ่ม option ชั่วคราว const newOpt = document.createElement('option'); newOpt.value = dataCat; newOpt.textContent = dataCat; // แทรกก่อน option __new__ const newOptEl = editCatSelect.querySelector('option[value="__new__"]'); editCatSelect.insertBefore(newOpt, newOptEl); editCatSelect.value = dataCat; } }); } // จัดการภาพขยายสลิป const viewSlipModal = document.getElementById('viewSlipModal'); if (viewSlipModal) { viewSlipModal.addEventListener('show.bs.modal', function (event) { const img = event.relatedTarget; const src = img.getAttribute('data-src'); document.getElementById('slip_full_image').src = src; }); } // ฟังก์ชันคัดกรองสินค้าตามหมวดหมู่ใน Admin (หลังบ้าน) function filterAdminCategory(category, btnElement) { // ปรับสถานะปุ่ม active document.querySelectorAll('.admin-category-btn').forEach(btn => { btn.classList.remove('active', 'btn-outline-danger'); btn.classList.add('btn-outline-secondary'); }); if (btnElement) { btnElement.classList.remove('btn-outline-secondary'); btnElement.classList.add('active', 'btn-outline-danger'); } // กรองแถวในตารางสินค้า const rows = document.querySelectorAll('.admin-product-row'); rows.forEach(row => { const rowCat = row.getAttribute('data-category'); if (category === 'all' || rowCat === category) { row.style.display = ''; } else { row.style.display = 'none'; } }); } </script> </body> </html>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.56 |
proxy
|
phpinfo
|
Settings