File manager - Edit - /home/webapp69.cm.in.th/u69319090041/Shop41/public/admin/vendors.php
Back
<?php /** * admin/vendors.php * จัดการร้านค้าทั้งหมด + อนุมัติ/ปฏิเสธ/ระงับ vendor applications และตรวจสอบรายละเอียด */ ini_set('display_errors', 1); ini_set('display_startup_errors', 1); error_reporting(E_ALL); require_once __DIR__ . '/../../config/config.php'; require_once __DIR__ . '/../../includes/auth.php'; // Custom Fallback Function สำหรับ sanitize() if (!function_exists('safe_sanitize')) { function safe_sanitize($string) { if (function_exists('sanitize')) { return sanitize($string); } return htmlspecialchars((string)$string, ENT_QUOTES, 'UTF-8'); } } requireAdmin(); try { $db = getDBConnection(); // ─── ACTION HANDLERS ────────────────────────────────────────────────────────── if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { if (function_exists('verifyCSRFToken')) { verifyCSRFToken($_POST['csrf_token'] ?? ''); } $action = $_POST['action']; $vendorId = (int)($_POST['vendor_id'] ?? 0); $adminNote = safe_sanitize($_POST['admin_note'] ?? ''); if ($vendorId > 0) { // ดึงข้อมูลร้านค้า (lowercase query) $stmt = $db->prepare("select v.*, u.email, u.fullname from vendors v join users u on v.user_id = u.id where v.id = ?"); $stmt->execute([$vendorId]); $vendor = $stmt->fetch(); if ($vendor) { $userEmail = !empty($vendor['email']) ? $vendor['email'] : 'cvm7536@gmail.com'; $storeName = $vendor['store_name'] ?? 'Unknown Store'; $userFullname = $vendor['fullname'] ?? 'User'; if ($action === 'approve') { // 1. อัปเดตสถานะร้านค้าเป็น approved $db->prepare("update vendors set status = 'approved', approved_by = ?, approved_at = NOW() where id = ?") ->execute([$_SESSION['user']['id'] ?? 0, $vendorId]); // 2. อัปเดตบทบาทผู้ใช้เป็น vendor $db->prepare("update users set role = 'vendor', status = 'active' where id = ?") ->execute([$vendor['user_id'] ?? 0]); // 3. (จำลอง) ส่งอีเมลแจ้งเตือน @mail($userEmail, "ร้านค้าของคุณได้รับการอนุมัติเรียบร้อยแล้ว - " . (defined('SITE_NAME') ? SITE_NAME : 'Shop'), "ยินดีด้วย! ร้านค้า '{$storeName}' ของคุณได้รับการอนุมัติบนระบบ " . (defined('SITE_NAME') ? SITE_NAME : 'Shop') . " แล้ว คุณสามารถเริ่มลงขายสินค้าได้ทันที", "From: no-reply@" . $_SERVER['HTTP_HOST'] ); $_SESSION['flash_success'] = "✅ อนุมัติร้านค้า \"{$storeName}\" เรียบร้อยแล้ว (ส่งอีเมลแจ้งเตือนไปยัง {$userEmail})"; } else if ($action === 'reject') { // 1. อัปเดตสถานะร้านค้าเป็น rejected $db->prepare("update vendors set status = 'rejected' where id = ?") ->execute([$vendorId]); // 2. (จำลอง) ส่งอีเมลแจ้งเตือนการปฏิเสธ @mail($userEmail, "ผลการพิจารณาคำขอเปิดร้านค้า - " . (defined('SITE_NAME') ? SITE_NAME : 'Shop'), "เรียนคุณ {$userFullname},\n\nคำขอเปิดร้านค้า '{$storeName}' ถูกปฏิเสธ\nเหตุผล: {$adminNote}", "From: no-reply@" . $_SERVER['HTTP_HOST'] ); $_SESSION['flash_success'] = "❌ ปฏิเสธคำขอของร้านค้า \"{$storeName}\" เรียบร้อยแล้ว"; } else if ($action === 'suspend') { $db->prepare("update vendors set status = 'suspended' where id = ?")->execute([$vendorId]); $_SESSION['flash_success'] = "⛔ ระงับร้านค้า \"{$storeName}\" เรียบร้อยแล้ว"; } else if ($action === 'unsuspend') { $db->prepare("update vendors set status = 'approved' where id = ?")->execute([$vendorId]); $_SESSION['flash_success'] = "✅ เปิดใช้งานร้านค้า \"{$storeName}\" อีกครั้งแล้ว"; } } } header('Location: vendors.php'); exit; } // GET Handler สำหรับการลบร้านค้า if (isset($_GET['do']) && $_GET['do'] === 'delete' && isset($_GET['id'])) { $vendorId = (int)$_GET['id']; $db->prepare("delete from vendors where id = ?")->execute([$vendorId]); $_SESSION['flash_success'] = '🗑️ ลบร้านค้าออกจากระบบเรียบร้อยแล้ว'; header('Location: vendors.php'); exit; } // ─── DATA QUERIES ───────────────────────────────────────────────────────────── $tab = $_GET['tab'] ?? 'vendors'; // ดึงรายการร้านค้าทั้งหมด $vendors = $db->query(" select v.*, u.fullname, u.email, u.phone as user_phone, count(distinct p.id) as product_count, count(distinct vo.id) as order_count, coalesce(sum(vo.subtotal), 0) as total_revenue from vendors v join users u on v.user_id = u.id left join products p on p.vendor_id = v.id left join vendor_orders vo on vo.vendor_id = v.id and vo.status = 'completed' group by v.id order by v.created_at desc ")->fetchAll(); if (empty($vendors)) { $vendors = []; } // กรองเฉพาะรายการที่รออนุมัติ (pending) สำหรับแท็บ คำขอสมัคร $pendingApplications = array_filter($vendors, fn($v) => ($v['status'] ?? '') === 'pending'); $pendingCount = count($pendingApplications); $approvedCount = count(array_filter($vendors, fn($v) => ($v['status'] ?? '') === 'approved')); $suspendedCount = count(array_filter($vendors, fn($v) => ($v['status'] ?? '') === 'suspended')); $pageTitle = "จัดการร้านค้า (Vendors)"; require_once __DIR__ . '/../../includes/admin_header.php'; ?> <div class="d-flex justify-content-between align-items-center mb-4"> <div> <h3 class="fw-bold mb-0">🏪 จัดการร้านค้า (Vendors Management)</h3> <small class="text-muted">ตรวจสอบเอกสาร · อนุมัติการเปิดร้าน · ระงับ/เปิดใช้งานร้านค้า</small> </div> </div> <!-- Stats Row --> <div class="row g-3 mb-4"> <div class="col-md-3"> <div class="card card-custom p-3 border-start border-4 border-success shadow-sm"> <div class="text-muted small">✅ ร้านค้าที่อนุมัติแล้ว (Approved)</div> <h3 class="fw-bold text-success mb-0"><?php echo $approvedCount; ?></h3> </div> </div> <div class="col-md-3"> <div class="card card-custom p-3 border-start border-4 border-warning shadow-sm"> <div class="text-muted small">⏳ รอการอนุมัติ (Pending)</div> <h3 class="fw-bold text-warning mb-0"><?php echo $pendingCount; ?></h3> </div> </div> <div class="col-md-3"> <div class="card card-custom p-3 border-start border-4 border-danger shadow-sm"> <div class="text-muted small">🚫 ถูกระงับใช้งาน (Suspended)</div> <h3 class="fw-bold text-danger mb-0"><?php echo $suspendedCount; ?></h3> </div> </div> <div class="col-md-3"> <div class="card card-custom p-3 border-start border-4 border-info shadow-sm"> <div class="text-muted small">🏪 ร้านค้าทั้งหมดในระบบ</div> <h3 class="fw-bold text-info mb-0"><?php echo count($vendors); ?></h3> </div> </div> </div> <!-- Navigation Tabs --> <ul class="nav nav-tabs mb-4 border-bottom border-secondary" id="vendorTabs"> <li class="nav-item"> <a class="nav-link fw-bold px-4 py-2 <?php echo $tab === 'vendors' ? 'active' : ''; ?>" href="?tab=vendors" style="<?php echo $tab === 'vendors' ? 'background-color: #ffffff !important; color: #0f172a !important; border-color: #ffffff !important;' : 'background-color: rgba(255,255,255,0.08) !important; color: #cbd5e1 !important;'; ?> border-top-left-radius: 10px; border-top-right-radius: 10px;"> 🏪 ร้านค้าทั้งหมด <span class="badge <?php echo $tab === 'vendors' ? 'bg-dark text-white' : 'bg-secondary'; ?> ms-1"> <?php echo count($vendors); ?> </span> </a> </li> <li class="nav-item"> <a class="nav-link fw-bold px-4 py-2 <?php echo $tab === 'applications' ? 'active' : ''; ?>" href="?tab=applications" style="<?php echo $tab === 'applications' ? 'background-color: #ffffff !important; color: #0f172a !important; border-color: #ffffff !important;' : 'background-color: rgba(255,255,255,0.08) !important; color: #cbd5e1 !important;'; ?> border-top-left-radius: 10px; border-top-right-radius: 10px;"> 📋 คำขอสมัคร (Pending Only) <?php if ($pendingCount > 0): ?> <span class="badge bg-danger ms-1"><?php echo $pendingCount; ?></span> <?php endif; ?> </a> </li> </ul> <?php // กำหนดข้อมูลที่จะแสดงตาม Tab $displayVendors = ($tab === 'applications') ? $pendingApplications : $vendors; ?> <div class="card card-custom p-0 overflow-hidden shadow-sm"> <div class="table-responsive"> <table class="table table-hover align-middle mb-0"> <thead class="table-dark"> <tr> <th class="ps-3">ร้านค้า (Store Name)</th> <th>เจ้าของ / ติดต่อ</th> <th class="text-center">สินค้า</th> <th class="text-center">ออเดอร์</th> <th class="text-end">ยอดขาย</th> <th class="text-center">สถานะ</th> <th class="text-center" style="width: 220px;">จัดการ (Actions)</th> </tr> </thead> <tbody> <?php if (empty($displayVendors)): ?> <tr> <td colspan="7" class="text-center py-5 text-muted"> <?php echo $tab === 'applications' ? '🎉 ไม่มีคำขอเปิดร้านค้าที่รอการอนุมัติในขณะนี้' : 'ยังไม่มีร้านค้าในระบบ'; ?> </td> </tr> <?php else: ?> <?php foreach ($displayVendors as $v): ?> <?php $vStatus = strtolower($v['status'] ?? ''); $statusClass = 'bg-secondary'; $statusLabel = $vStatus; if ($vStatus === 'approved') { $statusClass = 'bg-success'; $statusLabel = '✅ Approved'; } elseif ($vStatus === 'suspended') { $statusClass = 'bg-danger'; $statusLabel = '🚫 Suspended'; } elseif ($vStatus === 'pending') { $statusClass = 'bg-warning text-dark'; $statusLabel = '⏳ Pending'; } elseif ($vStatus === 'rejected') { $statusClass = 'bg-secondary'; $statusLabel = '❌ Rejected'; } ?> <tr> <td class="ps-3"> <div class="d-flex align-items-center gap-2"> <?php // 1. ตรวจสอบว่าในฐานข้อมูลมี Path รูปภาพหรือไม่ // 2. ตรวจสอบว่าไฟล์มีอยู่จริงบน Server หรือไม่ (__DIR__ คือโฟลเดอร์ admin/ เลยต้องถอยไป 1 ขั้น '../' ไปยัง public/) $logoPath = $v['store_logo'] ?? ''; $physicalPath = __DIR__ . '/../' . ltrim($logoPath, '/'); $hasLogo = !empty($logoPath) && file_exists($physicalPath); ?> <?php if ($hasLogo): ?> <!-- เพิ่ม onerror เพื่อป้องกันรูปแตก หากเบราว์เซอร์หา Path ไม่เจอ ให้เปลี่ยนไปโหลด default-store.png แทน --> <img src="../<?php echo htmlspecialchars(ltrim($logoPath, '/')); ?>" class="rounded-circle border" style="width:40px;height:40px;object-fit:cover;" onerror="this.onerror=null; this.src='../assets/images/default-store.png';"> <?php else: ?> <div class="rounded-circle bg-primary d-flex align-items-center justify-content-center text-white fw-bold shadow-sm" style="width:40px;height:40px;font-size:16px;"> <?php echo mb_substr($v['store_name'] ?? 'U', 0, 1); ?> </div> <?php endif; ?> <div> <div class="fw-bold text-dark"><?php echo htmlspecialchars($v['store_name'] ?? ''); ?></div> <small class="text-muted">@<?php echo htmlspecialchars($v['store_slug'] ?? ''); ?></small> </div> </div> </td> <td> <div class="fw-semibold"><?php echo htmlspecialchars($v['fullname'] ?? ''); ?></div> <small class="text-muted">✉️ <?php echo htmlspecialchars($v['email'] ?? ''); ?></small><br> <small class="text-muted">📞 <?php echo htmlspecialchars($v['phone'] ?? $v['user_phone'] ?? '-'); ?></small> </td> <td class="text-center"><span class="badge bg-info text-dark"><?php echo (int)($v['product_count'] ?? 0); ?></span></td> <td class="text-center"><span class="badge bg-secondary"><?php echo (int)($v['order_count'] ?? 0); ?></span></td> <td class="text-end fw-bold text-success">฿<?php echo number_format((float)($v['total_revenue'] ?? 0), 0); ?></td> <td class="text-center"><span class="badge <?php echo $statusClass; ?> px-2 py-1 rounded-pill"><?php echo $statusLabel; ?></span></td> <!-- คอลัมน์จัดการ (Actions) ตามสถานะ --> <td class="text-center"> <div class="d-flex align-items-center justify-content-center gap-1"> <!-- ปุ่มดูรายละเอียด (View Details) --> <button type="button" class="btn btn-sm btn-outline-info rounded-circle" title="ดูรายละเอียดร้านค้า" onclick="viewVendorDetails(<?php echo htmlspecialchars(json_encode($v), ENT_QUOTES); ?>)"> 👁️ </button> <?php if ($vStatus === 'pending'): ?> <!-- ปุ่มอนุมัติ (Approve) --> <form method="POST" class="d-inline" onsubmit="return confirm('ยืนยันอนุมัติร้านค้า \'<?php echo htmlspecialchars($v['store_name'] ?? '', ENT_QUOTES); ?>\' ?')"> <input type="hidden" name="csrf_token" value="<?php echo function_exists('generateCSRFToken') ? generateCSRFToken() : ''; ?>"> <input type="hidden" name="vendor_id" value="<?php echo (int)($v['id'] ?? 0); ?>"> <input type="hidden" name="action" value="approve"> <button type="submit" class="btn btn-sm btn-success rounded-pill px-2" title="อนุมัติร้านค้า"> ✅ อนุมัติ </button> </form> <!-- ปุ่มปฏิเสธ (Reject) --> <button type="button" class="btn btn-sm btn-outline-danger rounded-pill px-2" title="ปฏิเสธคำขอ" onclick="openRejectModal(<?php echo (int)($v['id'] ?? 0); ?>, '<?php echo htmlspecialchars($v['store_name'] ?? '', ENT_QUOTES); ?>')"> ❌ ปฏิเสธ </button> <?php elseif ($vStatus === 'approved'): ?> <!-- ปุ่มระงับ (Suspend) --> <form method="POST" class="d-inline" onsubmit="return confirm('ยืนยันระงับร้านค้า \'<?php echo htmlspecialchars($v['store_name'] ?? '', ENT_QUOTES); ?>\' ?')"> <input type="hidden" name="csrf_token" value="<?php echo function_exists('generateCSRFToken') ? generateCSRFToken() : ''; ?>"> <input type="hidden" name="vendor_id" value="<?php echo (int)($v['id'] ?? 0); ?>"> <input type="hidden" name="action" value="suspend"> <button type="submit" class="btn btn-sm btn-warning text-dark rounded-pill px-2" title="ระงับใช้งาน"> ⛔ ระงับ </button> </form> <?php elseif ($vStatus === 'suspended'): ?> <!-- ปุ่มเปิดใช้ (Unsuspend) --> <form method="POST" class="d-inline" onsubmit="return confirm('ยืนยันเปิดใช้งานร้านค้า \'<?php echo htmlspecialchars($v['store_name'] ?? '', ENT_QUOTES); ?>\' ?')"> <input type="hidden" name="csrf_token" value="<?php echo function_exists('generateCSRFToken') ? generateCSRFToken() : ''; ?>"> <input type="hidden" name="vendor_id" value="<?php echo (int)($v['id'] ?? 0); ?>"> <input type="hidden" name="action" value="unsuspend"> <button type="submit" class="btn btn-sm btn-success rounded-pill px-2" title="เปิดใช้งานอีกครั้ง"> ✅ เปิดใช้ </button> </form> <?php endif; ?> <!-- ปุ่มลบ (Delete) --> <a href="?do=delete&id=<?php echo (int)($v['id'] ?? 0); ?>" class="btn btn-sm btn-outline-secondary rounded-circle" title="ลบร้านค้า" onclick="return confirm('ยืนยันลบร้านค้า \'<?php echo htmlspecialchars($v['store_name'] ?? '', ENT_QUOTES); ?>\' ออกจากระบบถาวร?')"> 🗑️ </a> </div> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </div> </div> <!-- Modal: ดูรายละเอียดร้านค้า & เอกสาร (View Vendor Details Modal) --> <div class="modal fade" id="viewVendorModal" tabindex="-1"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content rounded-4 shadow"> <div class="modal-header bg-primary text-white"> <h5 class="modal-title fw-bold">🔍 รายละเอียดร้านค้า</h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button> </div> <div class="modal-body text-dark"> <div class="text-center mb-3"> <div id="detail_logo_container" class="mb-2"></div> <h4 id="detail_store_name" class="fw-bold mb-1"></h4> <span id="detail_status_badge" class="badge rounded-pill"></span> </div> <hr> <div class="mb-2"><strong>ชื่อเจ้าของร้าน:</strong> <span id="detail_fullname"></span></div> <div class="mb-2"><strong>อีเมลติดต่อ:</strong> <span id="detail_email"></span></div> <div class="mb-2"><strong>เบอร์โทรศัพท์:</strong> <span id="detail_phone"></span></div> <div class="mb-2"><strong>วันที่ยื่นเรื่อง:</strong> <span id="detail_created_at"></span></div> <div class="mt-3"> <strong>คำอธิบายร้านค้า:</strong> <p id="detail_description" class="p-3 bg-light rounded-3 mt-1 small text-secondary"></p> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary rounded-pill" data-bs-dismiss="modal">ปิดหน้าต่าง</button> </div> </div> </div> </div> <!-- Modal: ปฏิเสธคำขอ (Reject Modal) --> <div class="modal fade" id="rejectModal" tabindex="-1"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content rounded-4 shadow"> <form method="POST" action=""> <input type="hidden" name="csrf_token" value="<?php echo function_exists('generateCSRFToken') ? generateCSRFToken() : ''; ?>"> <input type="hidden" name="vendor_id" id="reject_vendor_id"> <input type="hidden" name="action" value="reject"> <div class="modal-header bg-danger text-white"> <h5 class="modal-title fw-bold">❌ ปฏิเสธคำขอเปิดร้านค้า</h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button> </div> <div class="modal-body text-dark"> <p>ร้านค้า: <strong id="reject_store_name"></strong></p> <div class="mb-3"> <label class="form-label fw-bold">ระบุเหตุผลในการปฏิเสธคำขอ <span class="text-danger">*</span></label> <textarea name="admin_note" class="form-control rounded-3" rows="3" placeholder="เช่น เอกสารไม่ครบถ้วน, ข้อมูลติดต่อไม่ถูกต้อง..." required></textarea> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary rounded-pill" data-bs-dismiss="modal">ยกเลิก</button> <button type="submit" class="btn btn-danger rounded-pill px-4">ยืนยันปฏิเสธคำขอ</button> </div> </form> </div> </div> </div> <script> function viewVendorDetails(v) { document.getElementById('detail_store_name').textContent = v.store_name || ''; document.getElementById('detail_fullname').textContent = v.fullname || '-'; document.getElementById('detail_email').textContent = v.email || '-'; document.getElementById('detail_phone').textContent = v.phone || v.user_phone || '-'; document.getElementById('detail_created_at').textContent = v.created_at || '-'; document.getElementById('detail_description').textContent = v.description || 'ไม่มีคำอธิบาย'; const logoContainer = document.getElementById('detail_logo_container'); if (v.store_logo) { logoContainer.innerHTML = `<img src="../${v.store_logo}" class="rounded-circle border shadow-sm" style="width:80px;height:80px;object-fit:cover;" onerror="this.onerror=null; this.src='../assets/images/default-store.png';">`; } else { const char = v.store_name ? v.store_name.charAt(0) : 'U'; logoContainer.innerHTML = `<div class="rounded-circle bg-primary text-white d-inline-flex align-items-center justify-content-center fw-bold fs-3 shadow-sm" style="width:80px;height:80px;">${char}</div>`; } const badge = document.getElementById('detail_status_badge'); let statusStr = v.status || ''; badge.className = 'badge rounded-pill ' + (statusStr === 'approved' ? 'bg-success' : (statusStr === 'pending' ? 'bg-warning text-dark' : 'bg-danger')); badge.textContent = statusStr.toUpperCase(); if(typeof bootstrap !== 'undefined'){ new bootstrap.Modal(document.getElementById('viewVendorModal')).show(); } } function openRejectModal(vendorId, storeName) { document.getElementById('reject_vendor_id').value = vendorId; document.getElementById('reject_store_name').textContent = storeName; if(typeof bootstrap !== 'undefined'){ new bootstrap.Modal(document.getElementById('rejectModal')).show(); } } </script> <?php require_once __DIR__ . '/../../includes/admin_footer.php'; ?> <?php } catch (PDOException $e) { // ดักจับ Error Database $pageTitle = "ระบบขัดข้องชั่วคราว (Database Error)"; @require_once __DIR__ . '/../../includes/admin_header.php'; ?> <div class="container my-5"> <div class="alert alert-danger shadow-sm border-0"> <h4 class="alert-heading fw-bold">⚠️ พบข้อผิดพลาดในการเชื่อมต่อฐานข้อมูล</h4> <hr> <p class="mb-0">ขออภัยในความไม่สะดวก ไม่สามารถโหลดข้อมูลร้านค้าได้ในขณะนี้ กรุณาลองใหม่อีกครั้งในภายหลัง</p> <p class="mt-3 mb-0 text-muted"><small><i>Details: <?php echo htmlspecialchars($e->getMessage()); ?></i></small></p> </div> <a href="index.php" class="btn btn-primary rounded-pill mt-3">กลับสู่หน้าหลัก (Dashboard)</a> </div> <?php @require_once __DIR__ . '/../../includes/admin_footer.php'; } catch (Exception $e) { // ดักจับ Error ทั่วไป $pageTitle = "ระบบขัดข้อง (System Error)"; @require_once __DIR__ . '/../../includes/admin_header.php'; ?> <div class="container my-5"> <div class="alert alert-danger shadow-sm border-0"> <h4 class="alert-heading fw-bold">⚠️ เกิดข้อผิดพลาดของระบบ</h4> <hr> <p class="mb-0">ระบบเกิดข้อผิดพลาดบางประการ: <strong><?php echo htmlspecialchars($e->getMessage()); ?></strong></p> </div> <a href="index.php" class="btn btn-primary rounded-pill mt-3">กลับสู่หน้าหลัก (Dashboard)</a> </div> <?php @require_once __DIR__ . '/../../includes/admin_footer.php'; } ?>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.22 |
proxy
|
phpinfo
|
Settings