File manager - Edit - /home/webapp69.cm.in.th/u69319090039/Shop39/database/app/Views/seller/dashboard.php
Back
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2"> <div> <h4 class="fw-bold m-0"><i class="bi bi-speedometer2 text-primary me-2"></i>Seller Dashboard</h4> <p class="text-muted small mb-0">ยินดีต้อนรับสู่ศูนย์ควบคุมและจัดการร้านค้าของคุณ</p> </div> <div class="d-flex align-items-center gap-2"> <a href="<?= APP_URL ?>/seller/orders" class="btn btn-outline-primary btn-sm rounded-pill px-3 fw-bold"> <i class="bi bi-receipt me-1"></i>ดูคำสั่งซื้อทั้งหมด </a> </div> </div> <?php if ($seller['status'] === 'suspended'): ?> <div class="alert alert-danger border-0 shadow-sm mb-4 p-4 rounded-4" role="alert"> <div class="d-flex align-items-center mb-2"> <i class="bi bi-exclamation-octagon-fill fs-3 text-danger me-2"></i> <h5 class="fw-bold m-0 text-danger">⚠️ ร้านค้าของคุณถูกระงับการใช้งานชั่วคราว (Suspended)</h5> </div> <p class="mb-2"><strong>เหตุผลจากระบบ:</strong> <?= Security::e($seller['suspended_reason'] ?? 'ละเมิดกฎ Marketplace') ?></p> <p class="small mb-0 text-muted">ระหว่างถูกระงับ: ร้านค้าจะไม่สามารถเพิ่มสินค้าใหม่ แก้ไขสินค้า หรือรับคำสั่งซื้อใหม่ได้ และสินค้าทั้งหมดในร้านจะถูกซ่อนจากหน้าร้าน</p> </div> <?php endif; ?> <!-- ══════════════════════════════════════════════ --> <!-- NEW ORDERS ALERT BANNER (Burgundy / Dark Red Highlight) --> <!-- ══════════════════════════════════════════════ --> <div id="newOrdersBanner" class="card border-0 shadow-sm rounded-4 mb-4 text-white overflow-hidden <?= ($newOrdersCount > 0) ? '' : 'd-none' ?>" style="background: linear-gradient(135deg, #741b27 0%, #a82435 100%);"> <div class="card-body p-4 position-relative"> <div class="d-flex align-items-center justify-content-between flex-wrap gap-3 position-relative z-1"> <div class="d-flex align-items-center gap-3"> <div class="rounded-circle bg-white text-danger d-flex align-items-center justify-content-center shadow" style="width: 58px; height: 58px; flex-shrink: 0;"> <i class="bi bi-bell-fill fs-3 text-danger animate-bell"></i> </div> <div> <div class="d-flex align-items-center gap-2"> <span class="badge bg-warning text-dark fw-bold px-3 py-1 rounded-pill"> <i class="bi bi-lightning-charge-fill me-1"></i>New Orders </span> <span class="text-white-50 small">อัปเดตแบบ Real-time</span> </div> <h5 class="fw-bold text-white mb-1 mt-2"> คุณมีคำสั่งซื้อใหม่ที่รอการจัดการ <span class="badge bg-white text-danger fs-6 rounded-pill px-2 py-1" id="bannerOrderCountBadge"><?= $newOrdersCount ?></span> รายการ </h5> <p class="text-white-50 mb-0 small"> You have <span id="bannerOrderCountText"><?= $newOrdersCount ?></span> new orders waiting to be processed. กรุณาตรวจสอบและเตรียมจัดส่งสินค้า </p> </div> </div> <div class="d-flex align-items-center gap-2"> <a href="<?= APP_URL ?>/seller/orders?status=pending" class="btn btn-warning btn-lg px-4 py-2 fw-bold rounded-pill text-dark shadow-sm"> <i class="bi bi-box-seam me-1"></i>ดูรายการสั่งซื้อ (View Orders) </a> </div> </div> </div> </div> <!-- Key Metrics Cards --> <div class="row g-4 mb-4"> <div class="col-md-4"> <div class="card border-0 shadow-sm rounded-4 p-4 text-white" style="background: linear-gradient(135deg, #0d6efd 0%, #0a58ca 100%);"> <div class="d-flex justify-content-between align-items-center mb-2"> <h6 class="text-white-50 fw-bold m-0">ยอดขายทั้งหมด</h6> <i class="bi bi-cash-stack fs-4 text-white-50"></i> </div> <h2 class="fw-bold m-0">฿<?= number_format($totalSales, 2) ?></h2> <div class="small text-white-50 mt-2">ยอดรวมคำสั่งซื้อที่เสร็จสมบูรณ์</div> </div> </div> <div class="col-md-4"> <a href="<?= APP_URL ?>/seller/orders" class="card border-0 shadow-sm rounded-4 p-4 text-white text-decoration-none" style="background: linear-gradient(135deg, #198754 0%, #146c43 100%);"> <div class="d-flex justify-content-between align-items-center mb-2"> <h6 class="text-white-50 fw-bold m-0">จำนวนคำสั่งซื้อ</h6> <i class="bi bi-receipt fs-4 text-white-50"></i> </div> <h2 class="fw-bold m-0"><?= number_format($totalOrders) ?> ออเดอร์</h2> <div class="small text-white-50 mt-2">รวมทุกสถานะคำสั่งซื้อ</div> </a> </div> <div class="col-md-4"> <div class="card border-0 shadow-sm rounded-4 p-4 text-white" style="background: linear-gradient(135deg, #212529 0%, #343a40 100%);"> <div class="d-flex justify-content-between align-items-center mb-2"> <h6 class="text-white-50 fw-bold m-0">สินค้าทั้งหมดในร้าน</h6> <i class="bi bi-box-seam fs-4 text-white-50"></i> </div> <h2 class="fw-bold m-0"><?= number_format($totalProducts) ?> รายการ</h2> <div class="small text-white-50 mt-2">สินค้าที่ลงขายในระบบ</div> </div> </div> </div> <div class="card border-0 shadow-sm rounded-4 p-4"> <div class="d-flex justify-content-between align-items-center mb-3"> <h5 class="fw-bold m-0">ภาพรวมการขายร้านค้า</h5> <a href="<?= APP_URL ?>/seller/products/create" class="btn btn-primary rounded-pill btn-sm px-3 fw-bold"> <i class="bi bi-plus-circle me-1"></i>ลงขายสินค้าใหม่ </a> </div> <p class="text-muted mb-0">คุณสามารถจัดการคลังสินค้า SKU กำหนดราคา และรับคำสั่งซื้อจากลูกค้าได้ในแถบเมนูด้านซ้าย</p> </div> <style> @keyframes bellSwing { 0%, 100% { transform: rotate(0); } 20%, 60% { transform: rotate(15deg); } 40%, 80% { transform: rotate(-15deg); } } .animate-bell { display: inline-block; animation: bellSwing 2s infinite ease-in-out; } </style> <!-- Live Order Notification Polling Script --> <script> (function() { let lastKnownOrderId = <?= (int)($latestOrderId ?? 0) ?>; let currentNewCount = <?= (int)($newOrdersCount ?? 0) ?>; const checkUrl = '<?= APP_URL ?>/seller/api/check-new-orders'; // Play subtle chime using Web Audio API (no external asset needed) function playOrderChime() { try { const ctx = new (window.AudioContext || window.webkitAudioContext)(); const now = ctx.currentTime; // Note 1: E5 (659.25Hz) const osc1 = ctx.createOscillator(); const gain1 = ctx.createGain(); osc1.type = 'sine'; osc1.frequency.setValueAtTime(659.25, now); gain1.gain.setValueAtTime(0.15, now); gain1.gain.exponentialRampToValueAtTime(0.001, now + 0.35); osc1.connect(gain1); gain1.connect(ctx.destination); osc1.start(now); osc1.stop(now + 0.35); // Note 2: A5 (880Hz) const osc2 = ctx.createOscillator(); const gain2 = ctx.createGain(); osc2.type = 'sine'; osc2.frequency.setValueAtTime(880, now + 0.12); gain2.gain.setValueAtTime(0.18, now + 0.12); gain2.gain.exponentialRampToValueAtTime(0.001, now + 0.6); osc2.connect(gain2); gain2.connect(ctx.destination); osc2.start(now + 0.12); osc2.stop(now + 0.6); } catch(e) { console.log('[Audio] Autoplay policy prevented chime:', e); } } // Show floating toast for incoming order function showNewOrderToast(order) { let toast = document.getElementById('liveOrderToast'); if (!toast) { toast = document.createElement('div'); toast.id = 'liveOrderToast'; toast.style.cssText = 'position:fixed;bottom:25px;right:25px;z-index:99999;min-width:320px;max-width:380px;background:#fff;border-radius:16px;box-shadow:0 12px 36px rgba(116,27,39,0.25);overflow:hidden;border:2px solid #a82435;transform:translateY(150%);transition:transform 0.4s cubic-bezier(.4,0,.2,1);'; document.body.appendChild(toast); } const num = order ? order.order_number : 'ออเดอร์ใหม่'; const amount = order ? parseFloat(order.total_amount).toLocaleString('th-TH', {minimumFractionDigits:2}) : ''; const customer = order ? order.customer_name : 'ลูกค้า'; toast.innerHTML = ` <div style="background:linear-gradient(135deg,#741b27,#a82435);padding:12px 16px;color:#fff;display:flex;align-items:center;justify-content:space-between;"> <span style="font-weight:700;font-size:14px;"><i class="bi bi-bell-fill me-2 text-warning"></i>🔔 มีคำสั่งซื้อใหม่! (New Order)</span> <button onclick="document.getElementById('liveOrderToast').style.transform='translateY(150%)'" style="background:none;border:none;color:#fff;font-size:20px;line-height:1;cursor:pointer;">×</button> </div> <div style="padding:14px 16px;"> <div style="font-weight:700;font-size:15px;color:#212529;">${num}</div> <div style="font-size:13px;color:#6c757d;margin-top:2px;">ลูกค้า: <strong>${customer}</strong> ${amount ? '• ยอดรวม: <span style="color:#198754;font-weight:700;">฿' + amount + '</span>' : ''}</div> </div> <div style="padding:0 16px 14px;display:flex;gap:8px;justify-content:flex-end;"> <a href="<?= APP_URL ?>/seller/orders" style="font-size:13px;padding:6px 18px;border-radius:99px;background:#a82435;color:#fff;text-decoration:none;font-weight:600;display:inline-flex;align-items:center;gap:6px;"> <i class="bi bi-box-seam"></i>ดูคำสั่งซื้อ (View Order) </a> </div> `; requestAnimationFrame(() => { requestAnimationFrame(() => { toast.style.transform = 'translateY(0)'; }); }); setTimeout(() => { if (toast) toast.style.transform = 'translateY(150%)'; }, 8000); } // Polling function (Runs every 15 seconds) function pollNewOrders() { fetch(checkUrl, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(res => res.json()) .then(data => { if (!data.success) return; const newCount = parseInt(data.count) || 0; const latestId = parseInt(data.latest_order_id) || 0; // Update UI badges const navBadge = document.getElementById('sellerNavOrderBadge'); const sideBadge = document.getElementById('sellerSidebarOrderBadge'); const banner = document.getElementById('newOrdersBanner'); const bannerBadge = document.getElementById('bannerOrderCountBadge'); const bannerText = document.getElementById('bannerOrderCountText'); if (navBadge) { navBadge.textContent = newCount; navBadge.classList.toggle('d-none', newCount <= 0); } if (sideBadge) { sideBadge.textContent = newCount; sideBadge.classList.toggle('d-none', newCount <= 0); } if (banner && bannerBadge && bannerText) { if (newCount > 0) { bannerBadge.textContent = newCount; bannerText.textContent = newCount; banner.classList.remove('d-none'); } else { banner.classList.add('d-none'); } } // If genuinely new order detected (higher ID and higher count) if (latestId > lastKnownOrderId && newCount > 0) { lastKnownOrderId = latestId; currentNewCount = newCount; playOrderChime(); showNewOrderToast(data.latest_order); } else { lastKnownOrderId = Math.max(lastKnownOrderId, latestId); currentNewCount = newCount; } }) .catch(err => console.error('[OrderNotification] Polling error:', err)); } // Start polling interval setInterval(pollNewOrders, 15000); })(); </script>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.24 |
proxy
|
phpinfo
|
Settings