File manager - Edit - /home/webapp69.cm.in.th/u69319090040/Shop/admin.php
Back
<?php // NEXUS Commerce - Platform Admin & Seller Portal (ระบบผู้ดูแลระบบและจัดการร้านค้า) if (session_status() === PHP_SESSION_NONE) { session_start(); } if (!isset($_SESSION['user_id']) || ($_SESSION['role'] !== 'admin' && $_SESSION['role'] !== 'seller')) { header("Location: admin_login.php"); exit; } ?> <!DOCTYPE html> <html lang="th"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SHOWPIPI Admin & Seller Portal — ระบบจัดการหลังบ้าน</title> <link rel="stylesheet" href="assets/css/style.css"> <script src="https://unpkg.com/@phosphor-icons/web"></script> <!-- Chart.js CDN for Analytics Graphs --> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <style> body { background-color: #080B11; color: #F8FAFC; font-family: 'Inter', sans-serif; } .admin-layout { display: grid; grid-template-columns: 270px 1fr; min-height: 100vh; } .admin-sidebar { background: rgba(15, 23, 42, 0.95); border-right: 1px solid rgba(255,255,255,0.1); padding: 1.5rem 1rem; display:flex; flex-direction:column; } .admin-menu-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.7rem 1rem; border-radius: 12px; color: #94A3B8; cursor: pointer; margin-bottom: 0.25rem; font-size:0.88rem; font-weight:600; transition: all 0.2s ease; } .admin-menu-item.active, .admin-menu-item:hover { background: rgba(6, 182, 212, 0.2); color: #FFF; border-left: 3px solid #06B6D4; } .admin-table, .data-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; } .admin-table th, .admin-table td, .data-table th, .data-table td { padding: 0.85rem 1rem; text-align: left; border-bottom: 1px solid rgba(255, 255, 255, 0.08); vertical-align: middle; } .admin-table th, .data-table th { background: rgba(255, 255, 255, 0.04); color: #94A3B8; font-weight: 700; } /* Modal Styling */ .admin-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.75); backdrop-filter: blur(8px); display: none; justify-content: center; align-items: center; z-index: 1000; } .admin-modal-overlay.open { display: flex; } .admin-modal-content { background: #0F172A; border: 1px solid rgba(255,255,255,0.15); border-radius: 16px; width: 100%; max-width: 540px; padding: 2rem; position: relative; box-shadow: 0 20px 40px rgba(0,0,0,0.5); } .admin-modal-close { position: absolute; top: 1.2rem; right: 1.2rem; background: rgba(255,255,255,0.1); border: none; color: #FFF; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; } /* Order Status Pills */ .order-status-tabs { display: flex; gap: 0.5rem; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 0.75rem; margin-bottom: 1.5rem; } .order-tab { padding: 0.5rem 1.25rem; border-radius: 20px; background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); color: #94A3B8; font-size: 0.85rem; font-weight: 600; cursor: pointer; } .order-tab.active { background: #7C3AED; color: #FFF; } .status-badge { padding: 3px 10px; border-radius: 12px; font-weight: 700; font-size: 0.75rem; display: inline-block; } .status-preparing { background: rgba(245, 158, 11, 0.2); color: #F59E0B; } .status-packed { background: rgba(6, 182, 212, 0.2); color: #06B6D4; } .status-shipped { background: rgba(124, 58, 237, 0.2); color: #7C3AED; } .status-delivered { background: rgba(16, 185, 129, 0.2); color: #10B981; } </style> </head> <body> <!-- Top-Right Red Exit Button --> <button onclick="if(document.referrer && document.referrer.includes(location.host)) { history.back(); } else { location.href='index.php'; }" style="position:fixed !important; top:1.25rem !important; right:1.5rem !important; z-index:999999 !important; background:rgba(244, 63, 94, 0.25); border:2px solid #F43F5E; color:#F43F5E; width:42px; height:42px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; cursor:pointer; font-size:1.4rem; transition:all 0.2s ease; box-shadow:0 0 15px rgba(244,63,94,0.5);" title="ปิด / ย้อนกลับ" onmouseover="this.style.background='#F43F5E'; this.style.color='#FFF'; this.style.transform='scale(1.1) rotate(90deg)';" onmouseout="this.style.background='rgba(244, 63, 94, 0.25)'; this.style.color='#F43F5E'; this.style.transform='none';"> <i class="ph ph-x"></i> </button> <div class="admin-layout"> <!-- Sidebar Navigation --> <aside class="admin-sidebar"> <div style="display:flex; align-items:center; gap:0.75rem; margin-bottom:1.5rem;"> <div class="brand-icon" style="background:var(--teal-accent);"><i class="ph ph-shield-check"></i></div> <div> <h3 style="font-size:1.05rem; font-weight:800;">Admin & Seller Center</h3> <span style="font-size:0.75rem; color:var(--teal-accent);">ศูนย์จัดการหลังบ้านรวม</span> </div> </div> <nav style="flex-grow:1; overflow-y:auto;"> <div style="font-size:0.7rem; color:var(--teal-accent); text-transform:uppercase; letter-spacing:1px; margin:0.4rem 0.5rem 0.25rem 0.5rem; font-weight:700;">Platform Admin (ผู้ดูแลระบบ)</div> <div class="admin-menu-item active" data-tab="dashboard"><i class="ph ph-chart-line-up"></i> ภาพรวมแพลตฟอร์ม (GMV Stats)</div> <div class="admin-menu-item" data-tab="sellers"><i class="ph ph-storefront"></i> จัดการบัญชีร้านค้า (Store Accounts)</div> <div class="admin-menu-item" data-tab="moderation"><i class="ph ph-shield-warning"></i> ตรวจสอบสินค้า (Moderation)</div> <div class="admin-menu-item" data-tab="commissions"><i class="ph ph-currency-dollar"></i> ค่าคอมมิชชั่น & การโอนเงิน</div> <div style="font-size:0.7rem; color:var(--purple-accent); text-transform:uppercase; letter-spacing:1px; margin:1.25rem 0.5rem 0.25rem 0.5rem; font-weight:700;">Seller Centre (จัดการร้านค้า)</div> <div class="admin-menu-item" data-tab="seller_overview"><i class="ph ph-squares-four"></i> ภาพรวมร้านค้า (Store Analytics)</div> <div class="admin-menu-item" data-tab="seller_orders"><i class="ph ph-package"></i> จัดการคำสั่งซื้อ (Orders)</div> <div class="admin-menu-item" data-tab="seller_products"><i class="ph ph-cube"></i> จัดการสินค้าในร้าน (Products)</div> <div class="admin-menu-item" data-tab="seller_inventory"><i class="ph ph-warning"></i> แจ้งเตือนสต็อก (Stock Intelligence)</div> <div class="admin-menu-item" data-tab="seller_ai"><i class="ph ph-sparkle"></i> AI Copywriter & SEO</div> <div class="admin-menu-item" data-tab="seller_vouchers"><i class="ph ph-ticket"></i> ศูนย์การตลาด & โค้ดส่วนลด</div> </nav> <div style="margin-top:auto; padding-top:1.25rem; border-top:1px solid rgba(255,255,255,0.1);"> <a href="index.php" style="color:var(--text-muted); text-decoration:none; font-size:0.85rem; display:flex; align-items:center; gap:6px; margin-bottom:0.75rem;"> <i class="ph ph-arrow-left"></i> กลับไปยังหน้าร้านค้า </a> <a href="#" onclick="window.nexusLogoutAdmin()" style="color:var(--rose-accent); text-decoration:none; font-size:0.85rem; display:flex; align-items:center; gap:6px; font-weight:bold;"> <i class="ph ph-sign-out"></i> ออกจากระบบหลังบ้าน </a> </div> </aside> <!-- Main Dynamic View Area --> <main style="padding:2rem; overflow-y:auto;" id="adminMainContainer"> <!-- Rendered via JS --> </main> </div> <!-- Admin Modal Container --> <div class="admin-modal-overlay" id="adminModalOverlay"> <div class="admin-modal-content"> <button class="admin-modal-close" onclick="document.getElementById('adminModalOverlay').classList.remove('open')"><i class="ph ph-x"></i></button> <div id="adminModalBody"></div> </div> </div> <script> document.addEventListener('DOMContentLoaded', () => { const container = document.getElementById('adminMainContainer'); const menuItems = document.querySelectorAll('.admin-menu-item'); const modalOverlay = document.getElementById('adminModalOverlay'); modalOverlay?.addEventListener('click', (e) => { if (e.target === modalOverlay) modalOverlay.classList.remove('open'); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modalOverlay) modalOverlay.classList.remove('open'); }); const urlParams = new URLSearchParams(window.location.search); let currentTab = urlParams.get('tab') || 'dashboard'; let activeOrderStatus = 'all'; let adminStoresList = []; const adminState = { products: [ { id: 1, title: 'NEXUS Pro ANC Headphones', store: 'CyberPulse Official', price: 2490, trust: 97, status: 'Approved' }, { id: 2, title: 'CyberDeck V1 Mechanical Keyboard', store: 'CyberPulse Official', price: 1890, trust: 95, status: 'Approved' }, { id: 3, title: 'Aura LightBar Pro Monitor Lamp', store: 'Aura Audio Lab', price: 1290, trust: 98, status: 'Approved' }, { id: 4, title: 'Unverified Wireless Earbuds', store: 'NeoTech Digital Shop', price: 690, trust: 60, status: 'Pending Review' } ], payouts: [ { id: 101, store: 'CyberPulse Official', sales: 184900, commission: 9245, net: 175655, status: 'Pending Payout' }, { id: 102, store: 'Aura Audio Lab', sales: 124500, commission: 6225, net: 118275, status: 'Paid' }, { id: 103, store: 'NeoTech Digital Shop', sales: 45000, commission: 2250, net: 42750, status: 'Pending Payout' } ] }; init(); function init() { menuItems.forEach(item => { if (item.dataset.tab === currentTab) { menuItems.forEach(m => m.classList.remove('active')); item.classList.add('active'); } item.addEventListener('click', () => { menuItems.forEach(m => m.classList.remove('active')); item.classList.add('active'); currentTab = item.dataset.tab; renderAdminView(); }); }); fetchStoreAccounts().then(() => { renderAdminView(); }); } async function fetchStoreAccounts() { try { const res = await fetch('api/admin_stores.php?action=list'); const data = await res.json(); if (data.status === 'success') { adminStoresList = data.data; } } catch (e) { console.error(e); } } function renderAdminView() { if (currentTab === 'dashboard') renderDashboard(); else if (currentTab === 'sellers') renderSellers(); else if (currentTab === 'moderation') renderModeration(); else if (currentTab === 'commissions') renderCommissions(); else if (currentTab === 'seller_overview') renderSellerOverview(); else if (currentTab === 'seller_orders') renderSellerOrders(); else if (currentTab === 'seller_products') renderSellerProducts(); else if (currentTab === 'seller_inventory') renderSellerInventoryAlerts(); else if (currentTab === 'seller_ai') renderSellerAITools(); else if (currentTab === 'seller_vouchers') renderSellerVouchers(); } // --- SECTION 1: PLATFORM ADMIN VIEWS --- // // 1.1 GMV Dashboard function renderDashboard() { container.innerHTML = ` <h1 style="font-size:1.8rem; font-weight:800; margin-bottom:0.5rem;">Platform Super Admin Command Center</h1> <p style="color:var(--text-muted); margin-bottom:1.5rem;">ระบบบริหารจัดการกลางสำหรับควบคุมยอดขาย ค่าคอมมิชชั่น ร้านค้า และผู้ใช้งาน</p> <div style="display:grid; grid-template-columns: repeat(3, 1fr); gap:1.25rem; margin-bottom:1.5rem;"> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">GROSS MARKETPLACE VOLUME (GMV)</span> <h2 style="font-size:2rem; font-weight:900; font-family:'Outfit'; color:var(--teal-accent);">฿4,820,500</h2> <span style="font-size:0.75rem; color:var(--emerald-accent);"><i class="ph ph-trend-up"></i> +24.5% Growth</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">PLATFORM COMMISSION (5%)</span> <h2 style="font-size:2rem; font-weight:900; font-family:'Outfit'; color:var(--emerald-accent);">฿241,025</h2> <span style="font-size:0.75rem; color:var(--emerald-accent);"><i class="ph ph-coins"></i> Net Platform Revenue</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">ACTIVE STORE ACCOUNTS</span> <h2 style="font-size:2rem; font-weight:900; font-family:'Outfit'; color:var(--gold-accent);">${adminStoresList.length} ร้านค้า</h2> <span style="font-size:0.75rem; color:var(--gold-accent);"><i class="ph ph-storefront"></i> Registered Store Accounts</span> </div> </div> <div class="glass-panel" style="padding:1.5rem; margin-bottom:1.5rem;"> <h3 style="font-size:1.1rem; font-weight:800; margin-bottom:0.4rem;"><i class="ph ph-chart-area-stepper" style="color:var(--teal-accent);"></i> กราฟเติบโตยอดขายรวมทั้งแพลตฟอร์ม (Platform GMV Growth Trend)</h3> <p style="font-size:0.78rem; color:var(--text-muted); margin-bottom:1rem;">ยอดขายสะสมและส่วนแบ่งรายได้แพลตฟอร์มรายเดือน</p> <div style="position:relative; height:240px;"> <canvas id="adminGmvChart"></canvas> </div> </div> <div class="glass-panel" style="padding:1.5rem;"> <h3 style="font-size:1.1rem; font-weight:800; margin-bottom:1rem;"><i class="ph ph-storefront"></i> รายชื่อบัญชีร้านค้าในระบบ (Store Accounts Summary)</h3> <table class="admin-table"> <thead> <tr> <th>ชื่อร้านค้า</th> <th>เจ้าของ / อีเมล</th> <th>จำนวนสินค้า</th> <th>Trust Score</th> <th>สถานะยืนยัน</th> </tr> </thead> <tbody> ${adminStoresList.map(s => ` <tr> <td> <div style="display:flex; align-items:center; gap:0.6rem;"> <img src="${s.logo_url}" style="width:32px; height:32px; border-radius:50%; object-fit:cover;"> <strong>${s.store_name}</strong> </div> </td> <td>${s.owner_name || '-'}<br><span style="font-size:0.75rem; color:var(--text-muted);">${s.owner_email || '-'}</span></td> <td>${s.product_count} รายการ</td> <td><strong style="color:var(--emerald-accent);">${s.trust_score}/100</strong></td> <td> <span style="padding:3px 10px; border-radius:10px; font-weight:bold; ${s.is_verified ? 'background:rgba(16,185,129,0.2); color:var(--emerald-accent);' : 'background:rgba(245,158,11,0.2); color:var(--gold-accent);'}"> ${s.is_verified ? 'Verified Official' : 'Pending Verification'} </span> </td> </tr> `).join('')} </tbody> </table> </div> `; setTimeout(() => { initAdminGmvChart(); }, 50); } function initAdminGmvChart() { const ctx = document.getElementById('adminGmvChart')?.getContext('2d'); if (!ctx) return; new Chart(ctx, { type: 'line', data: { labels: ['ม.ค.', 'ก.พ.', 'มี.ค.', 'เม.ย.', 'พ.ค.', 'มิ.ย.', 'ก.ค.', 'ส.ค.'], datasets: [{ label: 'Platform GMV (฿)', data: [1200000, 1850000, 2400000, 3100000, 3800000, 4200000, 4550000, 4820500], borderColor: '#7C3AED', backgroundColor: 'rgba(124, 58, 237, 0.2)', borderWidth: 3, fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: '#94A3B8' } } }, scales: { x: { ticks: { color: '#94A3B8' }, grid: { color: 'rgba(255,255,255,0.05)' } }, y: { ticks: { color: '#7C3AED' }, grid: { color: 'rgba(255,255,255,0.05)' } } } } }); } // 1.2 Store Accounts Control async function renderSellers() { await fetchStoreAccounts(); container.innerHTML = ` <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1.5rem;"> <div> <h1 style="font-size:1.8rem; font-weight:800;">จัดการบัญชีร้านค้า (Store Accounts Control Center)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">ระบบรวมการจัดการบัญชีร้านค้า อนุมัติสิทธิ์ และเพิ่มร้านค้าใหม่</p> </div> <button class="btn-primary" onclick="window.nexusOpenAddStoreModal()" style="padding:0.6rem 1.25rem; display:flex; align-items:center; gap:0.5rem; font-weight:bold;"> <i class="ph ph-plus-circle" style="font-size:1.2rem;"></i> เพิ่มบัญชีร้านค้าใหม่ </button> </div> <div style="display:grid; grid-template-columns: repeat(4, 1fr); gap:1rem; margin-bottom:1.5rem;"> <div class="glass-panel" style="padding:1rem;"> <span style="font-size:0.75rem; color:var(--text-muted);">ร้านค้าทั้งหมด</span> <h3 style="font-size:1.6rem; font-weight:800; color:var(--teal-accent);">${adminStoresList.length} ร้าน</h3> </div> <div class="glass-panel" style="padding:1rem;"> <span style="font-size:0.75rem; color:var(--text-muted);">ร้านค้ายืนยันแล้ว (Official)</span> <h3 style="font-size:1.6rem; font-weight:800; color:var(--emerald-accent);">${adminStoresList.filter(s => s.is_verified).length} ร้าน</h3> </div> <div class="glass-panel" style="padding:1rem;"> <span style="font-size:0.75rem; color:var(--text-muted);">รออนุมัติ / ตรวจสอบ</span> <h3 style="font-size:1.6rem; font-weight:800; color:var(--gold-accent);">${adminStoresList.filter(s => !s.is_verified).length} ร้าน</h3> </div> <div class="glass-panel" style="padding:1rem;"> <span style="font-size:0.75rem; color:var(--text-muted);">สินค้ารวมของทุกร้าน</span> <h3 style="font-size:1.6rem; font-weight:800; color:var(--purple-accent);">${adminStoresList.reduce((sum, s) => sum + parseInt(s.product_count || 0), 0)} รายการ</h3> </div> </div> <div class="glass-panel" style="padding:1rem;"> <table class="admin-table"> <thead> <tr> <th>ID ร้านค้า</th> <th>ชื่อร้านค้า</th> <th>เจ้าของร้าน / อีเมล</th> <th>ยอดสินค้า</th> <th>Trust Score</th> <th>สถานะยืนยัน</th> <th>การกระทำ</th> </tr> </thead> <tbody> ${adminStoresList.map(s => ` <tr> <td><code>#ST-${s.id}</code></td> <td> <div style="display:flex; align-items:center; gap:0.75rem;"> <img src="${s.logo_url}" style="width:38px; height:38px; border-radius:50%; object-fit:cover; border:1px solid var(--border-glass);"> <div> <strong style="font-size:0.95rem;">${s.store_name}</strong><br> <span style="font-size:0.75rem; color:var(--text-subtle);">Rating: ⭐ ${s.rating || 4.9}</span> </div> </div> </td> <td> <strong>${s.owner_name || 'N/A'}</strong><br> <span style="font-size:0.75rem; color:var(--text-muted);">${s.owner_email || 'N/A'}</span> </td> <td><strong style="color:var(--teal-accent);">${s.product_count} รายการ</strong></td> <td> <span style="color:var(--emerald-accent); font-weight:bold;">${s.trust_score}/100</span> </td> <td> <span style="padding:3px 10px; border-radius:12px; font-size:0.75rem; font-weight:bold; ${s.is_verified ? 'background:rgba(16,185,129,0.2); color:var(--emerald-accent);' : 'background:rgba(245,158,11,0.2); color:var(--gold-accent);'}"> ${s.is_verified ? 'Verified Official' : 'Pending Verification'} </span> </td> <td> <div style="display:flex; gap:0.35rem;"> <button class="btn-glass" style="padding:4px 8px; font-size:0.75rem;" onclick="window.nexusSwitchTab('seller_overview')" title="เข้าดู Seller Centre"> <i class="ph ph-storefront"></i> จัดการร้าน </button> <button class="btn-glass" style="padding:4px 8px; font-size:0.75rem;" onclick="window.nexusOpenEditStoreModal(${s.id})" title="แก้ไขร้านค้า"> <i class="ph ph-pencil-simple"></i> </button> <button class="btn-glass" style="padding:4px 8px; font-size:0.75rem; color:${s.is_verified ? 'var(--gold-accent)' : 'var(--emerald-accent)'};" onclick="window.nexusToggleStoreVerify(${s.id})" title="สลับอนุมัติ"> <i class="ph ph-shield-check"></i> </button> <button class="btn-glass" style="padding:4px 8px; font-size:0.75rem; color:var(--rose-accent);" onclick="window.nexusDeleteStore(${s.id})" title="ลบร้านค้า"> <i class="ph ph-trash"></i> </button> </div> </td> </tr> `).join('')} </tbody> </table> </div> `; } window.nexusSwitchTab = function(tabName) { currentTab = tabName; menuItems.forEach(m => { m.classList.toggle('active', m.dataset.tab === tabName); }); renderAdminView(); }; // Add & Edit Store Modals window.nexusOpenAddStoreModal = function() { const modal = document.getElementById('adminModalOverlay'); const modalBody = document.getElementById('adminModalBody'); if (!modal || !modalBody) return; modalBody.innerHTML = ` <div style="text-align:left;"> <h2 style="font-size:1.3rem; font-weight:800; margin-bottom:1rem; display:flex; align-items:center; gap:0.5rem;"> <i class="ph ph-storefront" style="color:var(--teal-accent);"></i> เพิ่มบัญชีร้านค้าใหม่ (Create Store Account) </h2> <form id="addStoreForm"> <div style="margin-bottom:0.85rem;"> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">ชื่อร้านค้า (Store Name) *</label> <input type="text" name="store_name" required placeholder="เช่น CyberTech Official Store" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.85rem; margin-bottom:0.85rem;"> <div> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">ชื่อเจ้าของร้าน (Owner Name)</label> <input type="text" name="owner_name" placeholder="สมชาย ใจดี" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> <div> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">อีเมลผู้ดูแลร้าน (Email) *</label> <input type="email" name="owner_email" required placeholder="owner@store.com" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> </div> <div style="margin-bottom:0.85rem;"> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">URL โลโก้ร้านค้า</label> <input type="url" name="logo_url" value="https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=150" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> <button type="submit" class="btn-primary" style="width:100%; padding:0.75rem; font-weight:bold;">สร้างบัญชีร้านค้า</button> </form> </div> `; modal.classList.add('open'); document.getElementById('addStoreForm')?.addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(e.target); const res = await fetch('api/admin_stores.php?action=add', { method: 'POST', body: formData }); const data = await res.json(); if (data.status === 'success') { alert(data.message); modal.classList.remove('open'); renderSellers(); } }); }; window.nexusOpenEditStoreModal = function(id) { const store = adminStoresList.find(s => s.id == id); if (!store) return; const modal = document.getElementById('adminModalOverlay'); const modalBody = document.getElementById('adminModalBody'); if (!modal || !modalBody) return; modalBody.innerHTML = ` <div style="text-align:left;"> <h2 style="font-size:1.3rem; font-weight:800; margin-bottom:1rem; display:flex; align-items:center; gap:0.5rem;"> <i class="ph ph-pencil-simple" style="color:var(--teal-accent);"></i> แก้ไขบัญชีร้านค้า (#ST-${store.id}) </h2> <form id="editStoreForm"> <input type="hidden" name="id" value="${store.id}"> <div style="margin-bottom:0.85rem;"> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">ชื่อร้านค้า (Store Name)</label> <input type="text" name="store_name" value="${store.store_name}" required style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.85rem; margin-bottom:0.85rem;"> <div> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">ชื่อเจ้าของร้าน</label> <input type="text" name="owner_name" value="${store.owner_name || ''}" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> <div> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">อีเมลผู้ดูแลร้าน</label> <input type="email" name="owner_email" value="${store.owner_email || ''}" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.65rem; border-radius:8px; color:#FFF;"> </div> </div> <button type="submit" class="btn-primary" style="width:100%; padding:0.75rem; font-weight:bold;">บันทึกการแก้ไข</button> </form> </div> `; modal.classList.add('open'); document.getElementById('editStoreForm')?.addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(e.target); const res = await fetch('api/admin_stores.php?action=update', { method: 'POST', body: formData }); const data = await res.json(); if (data.status === 'success') { alert(data.message); modal.classList.remove('open'); renderSellers(); } }); }; window.nexusToggleStoreVerify = async function(id) { const formData = new FormData(); formData.append('id', id); await fetch('api/admin_stores.php?action=toggle_verify', { method: 'POST', body: formData }); renderSellers(); }; window.nexusDeleteStore = async function(id) { if (confirm('ยืนยันการลบบัญชีร้านค้านี้หรือไม่?')) { const formData = new FormData(); formData.append('id', id); const res = await fetch('api/admin_stores.php?action=delete', { method: 'POST', body: formData }); const data = await res.json(); if (data.status === 'success') { alert(data.message); renderSellers(); } } }; // 1.3 Product Moderation function renderModeration() { container.innerHTML = ` <div style="margin-bottom:1.5rem;"> <h1 style="font-size:1.8rem; font-weight:800;">ตรวจสอบและอนุมัติสินค้า (Content & Listing Moderation)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">ตรวจสอบคุณภาพสินค้า ป้องกันสินค้าปลอมแปลง และระงับสินค้าที่ผิดนโยบายแพลตฟอร์ม</p> </div> <div class="glass-panel" style="padding:1rem;"> <table class="admin-table"> <thead> <tr> <th>ID</th> <th>ชื่อสินค้า</th> <th>ร้านค้าที่ลงขาย</th> <th>ราคาขาย</th> <th>คะแนน Trust</th> <th>สถานะการตรวจสอบ</th> <th>การกระทำ</th> </tr> </thead> <tbody> ${adminState.products.map(p => ` <tr> <td><code>#PRD-${p.id}</code></td> <td><strong>${p.title}</strong></td> <td>${p.store}</td> <td><strong class="current-price">฿${numberFormat(p.price)}</strong></td> <td><span style="color:var(--emerald-accent); font-weight:bold;">${p.trust}/100</span></td> <td> <span style="padding:3px 10px; border-radius:12px; font-size:0.75rem; font-weight:bold; ${p.status === 'Approved' ? 'background:rgba(16,185,129,0.2); color:var(--emerald-accent);' : 'background:rgba(245,158,11,0.2); color:var(--gold-accent);'}"> ${p.status} </span> </td> <td> <div style="display:flex; gap:0.4rem;"> ${p.status !== 'Approved' ? ` <button class="btn-primary" style="padding:4px 10px; font-size:0.75rem;" onclick="window.nexusApproveProduct(${p.id})"> <i class="ph ph-check"></i> อนุมัติผ่าน </button> ` : ` <button class="btn-glass" style="padding:4px 10px; font-size:0.75rem; color:var(--rose-accent);" onclick="window.nexusFlagProduct(${p.id})"> <i class="ph ph-flag"></i> ระงับรายการ </button> `} </div> </td> </tr> `).join('')} </tbody> </table> </div> `; } window.nexusApproveProduct = function(id) { const p = adminState.products.find(item => item.id === id); if (p) { p.status = 'Approved'; alert(`อนุมัติรายการสินค้า "${p.title}" เรียบร้อยแล้ว`); renderModeration(); } }; window.nexusFlagProduct = function(id) { const p = adminState.products.find(item => item.id === id); if (p && confirm(`ยืนยันการระงับรายการสินค้า "${p.title}" หรือไม่?`)) { p.status = 'Flagged'; renderModeration(); } }; // 1.4 Commissions & Payouts function renderCommissions() { const totalSales = adminState.payouts.reduce((sum, i) => sum + i.sales, 0); const totalCommissions = adminState.payouts.reduce((sum, i) => sum + i.commission, 0); const totalNetPayouts = adminState.payouts.reduce((sum, i) => sum + i.net, 0); container.innerHTML = ` <div style="margin-bottom:1.5rem;"> <h1 style="font-size:1.8rem; font-weight:800;">ค่าคอมมิชชั่น & การโอนเงินให้ร้านค้า (Platform Commission & Seller Payouts)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">ระบบคำนวณหักค่าคอมมิชชั่นแพลตฟอร์ม 5% และสั่งโอนเงินรายได้ให้แก่ร้านค้า</p> </div> <div style="display:grid; grid-template-columns: repeat(3, 1fr); gap:1.25rem; margin-bottom:1.5rem;"> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">ยอดขายรวมในรอบโอน</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--teal-accent);">฿${numberFormat(totalSales)}</h2> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">รายได้ค่าคอมมิชชั่น (5%)</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--emerald-accent);">฿${numberFormat(totalCommissions)}</h2> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">ยอดรอโอนให้ร้านค้า</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--gold-accent);">฿${numberFormat(totalNetPayouts)}</h2> </div> </div> <div class="glass-panel" style="padding:1rem;"> <table class="admin-table"> <thead> <tr> <th>รอบโอน ID</th> <th>ชื่อร้านค้า</th> <th>ยอดขายรวม</th> <th>ค่าคอมมิชชั่น 5%</th> <th>ยอดโอนสุทธิ (Net Payout)</th> <th>สถานะโอนเงิน</th> <th>การกระทำ</th> </tr> </thead> <tbody> ${adminState.payouts.map(po => ` <tr> <td><code>#PAY-${po.id}</code></td> <td><strong>${po.store}</strong></td> <td>฿${numberFormat(po.sales)}</td> <td><span style="color:var(--emerald-accent); font-weight:bold;">-฿${numberFormat(po.commission)}</span></td> <td><strong class="current-price" style="font-size:1.05rem;">฿${numberFormat(po.net)}</strong></td> <td> <span style="padding:3px 10px; border-radius:12px; font-size:0.75rem; font-weight:bold; ${po.status === 'Paid' ? 'background:rgba(16,185,129,0.2); color:var(--emerald-accent);' : 'background:rgba(245,158,11,0.2); color:var(--gold-accent);'}"> ${po.status} </span> </td> <td> ${po.status !== 'Paid' ? ` <button class="btn-primary" style="padding:4px 10px; font-size:0.75rem;" onclick="window.nexusExecutePayout(${po.id})"> <i class="ph ph-paper-plane-tilt"></i> อนุมัติโอนเงิน </button> ` : ` <span style="font-size:0.75rem; color:var(--text-muted);"><i class="ph ph-check-double"></i> โอนสำเร็จแล้ว</span> `} </td> </tr> `).join('')} </tbody> </table> </div> `; } window.nexusExecutePayout = function(id) { const po = adminState.payouts.find(item => item.id === id); if (po && confirm(`ยืนยันการอนุมัติสั่งโอนเงินสุทธิ ฿${numberFormat(po.net)} ให้แก่ร้านค้า "${po.store}" หรือไม่?`)) { po.status = 'Paid'; alert(`ดำเนินการสั่งโอนเงินสุทธิ ฿${numberFormat(po.net)} ให้แก่ร้านค้า "${po.store}" เรียบร้อยแล้ว`); renderCommissions(); } }; // --- SECTION 2: INTEGRATED SELLER CENTRE VIEWS --- // // 2.1 Seller Overview & Analytics function renderSellerOverview() { container.innerHTML = ` <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1.5rem;"> <div> <h1 style="font-size:1.8rem; font-weight:800;">ภาพรวมร้านค้าและวิเคราะห์ยอดขาย (Store Analytics)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">ข้อมูลสรุปยอดขาย ประสิทธิภาพการจัดส่ง และแนวโน้มคำสั่งซื้อของร้านค้า</p> </div> <button class="btn-primary" style="padding:0.5rem 1.1rem; font-size:0.85rem;" onclick="window.nexusOpenAddProductModal()"> <i class="ph ph-plus"></i> เพิ่มสินค้าใหม่ </button> </div> <div style="display:grid; grid-template-columns: repeat(4, 1fr); gap:1.25rem; margin-bottom:1.5rem;"> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">ยอดขายรวมสุทธิ (Net Sales)</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--emerald-accent);">฿184,900</h2> <span style="font-size:0.75rem; color:var(--emerald-accent);"><i class="ph ph-trend-up"></i> +18.4% จากเดือนที่แล้ว</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">คำสั่งซื้อทั้งหมด (Completed Orders)</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit';">482 ออเดอร์</h2> <span style="font-size:0.75rem; color:var(--teal-accent);"><i class="ph ph-check-circle"></i> 99.2% อัตราจัดส่งตรงเวลา</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">ยอดขายเฉลี่ยต่อออเดอร์ (AOV)</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--gold-accent);">฿1,240</h2> <span style="font-size:0.75rem; color:var(--gold-accent);"><i class="ph ph-trend-up"></i> +5.2% เพิ่มขึ้น</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">อัตราผู้ซื้อเข้าชมร้านค้า (Conversion)</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--rose-accent);">4.85%</h2> <span style="font-size:0.75rem; color:var(--rose-accent);"><i class="ph ph-users"></i> 12,400 ผู้เข้าชม</span> </div> </div> <div style="display:grid; grid-template-columns: 1.4fr 0.6fr; gap:1.5rem;"> <div class="glass-panel" style="padding:1.5rem;"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;"> <div> <h3 style="font-size:1.1rem; font-weight:800;"><i class="ph ph-chart-line-up" style="color:var(--teal-accent);"></i> กราฟแนวโน้มยอดขายและคำสั่งซื้อ (Sales Trend)</h3> <p style="font-size:0.78rem; color:var(--text-muted);">เปรียบเทียบยอดขาย (บาท) และจำนวนออเดอร์</p> </div> </div> <div style="position:relative; height:260px;"> <canvas id="sellerSalesChart"></canvas> </div> </div> <div class="glass-panel" style="padding:1.5rem;"> <h3 style="font-size:1.1rem; font-weight:800; margin-bottom:0.4rem;"><i class="ph ph-chart-pie" style="color:var(--gold-accent);"></i> สัดส่วนยอดขายตามหมวดหมู่</h3> <p style="font-size:0.78rem; color:var(--text-muted); margin-bottom:1rem;">Category Sales Distribution</p> <div style="position:relative; height:220px; display:flex; justify-content:center;"> <canvas id="sellerCategoryChart"></canvas> </div> </div> </div> `; setTimeout(() => { initSellerCharts(); }, 50); } function initSellerCharts() { const ctxSales = document.getElementById('sellerSalesChart')?.getContext('2d'); const ctxCat = document.getElementById('sellerCategoryChart')?.getContext('2d'); if (!ctxSales || !ctxCat) return; new Chart(ctxSales, { type: 'line', data: { labels: ['จันทร์', 'อังคาร', 'พุธ', 'พฤหัส', 'ศุกร์', 'เสาร์', 'อาทิตย์'], datasets: [ { label: 'ยอดขาย (บาท ฿)', data: [18200, 24500, 14100, 29800, 21000, 32400, 28900], borderColor: '#06B6D4', backgroundColor: 'rgba(6, 182, 212, 0.15)', borderWidth: 3, fill: true, tension: 0.4 }, { label: 'จำนวนออเดอร์ (ชิ้น)', data: [18, 26, 12, 31, 22, 36, 30], borderColor: '#F59E0B', borderWidth: 2, borderDash: [5, 5] } ] }, options: { responsive: true, maintainAspectRatio: false } }); new Chart(ctxCat, { type: 'doughnut', data: { labels: ['Audio & Headset', 'Gaming Gear', 'Tech & Gadgets', 'Cyber Fashion'], datasets: [{ data: [45, 25, 18, 12], backgroundColor: ['#7C3AED', '#06B6D4', '#F59E0B', '#10B981'], borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: false } }); } // 2.2 Seller Orders & Fulfillment async function renderSellerOrders() { container.innerHTML = ` <div style="margin-bottom:1rem;"> <h1 style="font-size:1.8rem; font-weight:800;">จัดการคำสั่งซื้อ (Order & Fulfillment Center)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">ตรวจสอบสถานะคำสั่งซื้อ พิมพ์ใบปะหน้าพัสดุ และอัปเดตเลขพัสดุ</p> </div> <div class="order-status-tabs"> <button class="order-tab ${activeOrderStatus === 'all' ? 'active' : ''}" onclick="window.nexusFilterOrderTab('all')">ทั้งหมด (All)</button> <button class="order-tab ${activeOrderStatus === 'preparing' ? 'active' : ''}" onclick="window.nexusFilterOrderTab('preparing')">ที่ต้องจัดส่ง (To Ship)</button> <button class="order-tab ${activeOrderStatus === 'packed' ? 'active' : ''}" onclick="window.nexusFilterOrderTab('packed')">แพ็คเรียบร้อย (Packed)</button> <button class="order-tab ${activeOrderStatus === 'shipped' ? 'active' : ''}" onclick="window.nexusFilterOrderTab('shipped')">จัดส่งแล้ว (Shipped)</button> <button class="order-tab ${activeOrderStatus === 'delivered' ? 'active' : ''}" onclick="window.nexusFilterOrderTab('delivered')">สำเร็จแล้ว (Completed)</button> </div> <div class="glass-panel" style="padding:1rem;"> <table class="data-table"> <thead> <tr> <th>เลขที่ออเดอร์</th> <th>ลูกค้า</th> <th>รายการสินค้า</th> <th>ยอดรวม</th> <th>สถานะ</th> <th>เลขพัสดุ</th> <th>การกระทำ</th> </tr> </thead> <tbody id="ordersTbody"> <tr><td colspan="7" style="text-align:center; padding:2rem;">กำลังโหลดออเดอร์...</td></tr> </tbody> </table> </div> `; try { const res = await fetch(`api/seller_orders.php?status=${activeOrderStatus}`); const data = await res.json(); const tbody = document.getElementById('ordersTbody'); if (data.status === 'success' && tbody) { if (data.data.length === 0) { tbody.innerHTML = `<tr><td colspan="7" style="text-align:center; padding:2rem; color:var(--text-subtle);">ไม่มีคำสั่งซื้อในสถานะนี้</td></tr>`; return; } tbody.innerHTML = data.data.map(ord => ` <tr> <td><strong>#${ord.order_sn}</strong><br><span style="font-size:0.75rem; color:var(--text-muted);">${ord.created_at}</span></td> <td>${ord.customer_name}<br><span style="font-size:0.75rem; color:var(--text-subtle);">${ord.shipping_address}</span></td> <td>${(ord.items || []).map(i => `<div>• ${i.title} x${i.quantity}</div>`).join('')}</td> <td><strong class="current-price" style="font-size:0.95rem;">฿${numberFormat(ord.final_amount)}</strong></td> <td><span class="status-badge status-${ord.status}">${ord.status.toUpperCase()}</span></td> <td><code>${ord.tracking_number || '-'}</code></td> <td> <div style="display:flex; gap:0.4rem;"> <button class="btn-glass" style="padding:4px 8px; font-size:0.75rem;" onclick="window.nexusPrintPackingSlip('${ord.order_sn}', '${ord.customer_name}', '${ord.shipping_address}', '${ord.final_amount}')" title="พิมพ์ใบปะหน้า"> <i class="ph ph-printer"></i> ใบปะหน้า </button> ${ord.status === 'preparing' ? ` <button class="btn-primary" style="padding:4px 8px; font-size:0.75rem;" onclick="window.nexusUpdateOrderStatus(${ord.id}, 'shipped')"> จัดส่งพัสดุ </button> ` : ''} </div> </td> </tr> `).join(''); } } catch (e) { console.error(e); } } window.nexusFilterOrderTab = function(status) { activeOrderStatus = status; renderSellerOrders(); }; window.nexusPrintPackingSlip = function(orderSn, name, address, amount) { const modal = document.getElementById('adminModalOverlay'); const modalBody = document.getElementById('adminModalBody'); if (!modal || !modalBody) return; modalBody.innerHTML = ` <div style="background:#FFF; color:#000; padding:2rem; border-radius:12px; font-family:sans-serif; text-align:left;"> <div style="display:flex; justify-content:space-between; border-bottom:2px solid #000; padding-bottom:1rem; margin-bottom:1rem;"> <h2>SHOWPIPI EXPRESS PACKING SLIP</h2> <h3>ORDER #${orderSn}</h3> </div> <div style="margin-bottom:1.5rem;"> <strong>ผู้รับ (RECIPIENT):</strong><br> <span style="font-size:1.1rem; font-weight:bold;">${name}</span><br> <span>${address}</span> </div> <div style="border:1px dashed #000; padding:1rem; border-radius:8px; margin-bottom:1.5rem;"> <strong>ยอดเก็บเงินปลายทาง / ชำระแล้ว:</strong> ฿${numberFormat(amount)} THB<br> <strong>TRACKING NUMBER:</strong> TH902847291 </div> <button onclick="window.print()" style="background:#000; color:#FFF; padding:8px 20px; border:none; border-radius:6px; font-weight:bold; cursor:pointer;">พิมพ์เอกสาร (Print)</button> </div> `; modal.classList.add('open'); }; window.nexusUpdateOrderStatus = async function(orderId, newStatus) { const formData = new FormData(); formData.append('order_id', orderId); formData.append('new_status', newStatus); const res = await fetch('api/seller_orders.php?action=update_status', { method: 'POST', body: formData }); const data = await res.json(); if (data.status === 'success') { alert(`อัปเดตสถานะออเดอร์เป็น ${newStatus} เรียบร้อยแล้ว! เลขพัสดุ: ${data.tracking_number}`); renderSellerOrders(); } }; // 2.3 Seller Products Catalog async function renderSellerProducts() { container.innerHTML = ` <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1.5rem;"> <div> <h1 style="font-size:1.8rem; font-weight:800;">จัดการคลังสินค้าในร้าน (Product Catalog)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">เพิ่มสินค้า แก้ไขสต็อกสินค้า และจัดการตัวเลือกสี/ไซส์</p> </div> <button class="btn-primary" style="padding:0.5rem 1.1rem; font-size:0.85rem;" onclick="window.nexusOpenAddProductModal()"> <i class="ph ph-plus"></i> เพิ่มสินค้าใหม่ </button> </div> <div class="glass-panel" style="padding:1rem;"> <table class="data-table"> <thead> <tr> <th>รูปสินค้า</th> <th>ชื่อสินค้า</th> <th>SKU</th> <th>ราคาขาย</th> <th>คงเหลือสต็อก</th> <th>Trust Score</th> <th>ยอดขาย</th> <th>การกระทำ</th> </tr> </thead> <tbody id="prodTbody"> <tr><td colspan="8" style="text-align:center; padding:2rem;">กำลังโหลดสินค้า...</td></tr> </tbody> </table> </div> `; try { const res = await fetch('api/seller_products.php?action=list'); const data = await res.json(); const tbody = document.getElementById('prodTbody'); if (data.status === 'success' && tbody) { tbody.innerHTML = data.data.map(p => ` <tr> <td><img src="${p.image_url}" style="width:45px; height:45px; object-fit:cover; border-radius:6px;"></td> <td><strong>${p.title}</strong><br><span style="font-size:0.75rem; color:var(--text-muted);">${p.category_name}</span></td> <td><code>${p.sku}</code></td> <td><strong class="current-price">฿${numberFormat(p.price)}</strong></td> <td> <input type="number" value="${p.stock}" style="width:65px; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); border-radius:6px; color:#FFF; padding:3px 6px; font-weight:bold;" onchange="window.nexusQuickUpdateStock(${p.id}, this.value)"> </td> <td><span class="status-badge" style="background:rgba(16,185,129,0.2); color:var(--emerald-accent);">${p.trust_score}/100</span></td> <td>${p.sales_count} ชิ้น</td> <td> <button class="icon-btn" style="width:30px; height:30px; color:var(--rose-accent);" onclick="window.nexusDeleteProduct(${p.id})"><i class="ph ph-trash"></i></button> </td> </tr> `).join(''); } } catch (e) { console.error(e); } } window.nexusQuickUpdateStock = async function(id, newStock) { const formData = new FormData(); formData.append('id', id); formData.append('stock', newStock); await fetch('api/seller_products.php?action=update_stock', { method: 'POST', body: formData }); }; window.nexusDeleteProduct = async function(id) { if (confirm('ยืนยันลบสินค้านี้ออกจากคลังหรือไม่?')) { const formData = new FormData(); formData.append('id', id); await fetch('api/seller_products.php?action=delete', { method: 'POST', body: formData }); renderSellerProducts(); } }; window.nexusOpenAddProductModal = function() { const modal = document.getElementById('adminModalOverlay'); const modalBody = document.getElementById('adminModalBody'); if (!modal || !modalBody) return; modalBody.innerHTML = ` <div style="text-align:left;"> <h2 style="font-size:1.4rem; font-weight:800; margin-bottom:1rem;"><i class="ph ph-plus-circle"></i> เพิ่มสินค้าใหม่เข้าร้านค้า</h2> <form id="addProductForm"> <div style="margin-bottom:0.85rem;"> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">ชื่อสินค้า</label> <input type="text" name="title" required style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"> </div> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.85rem; margin-bottom:0.85rem;"> <div> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">ราคาขาย (฿)</label> <input type="number" name="price" required style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"> </div> <div> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">จำนวนสต็อก</label> <input type="number" name="stock" value="50" required style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"> </div> </div> <div style="margin-bottom:0.85rem;"> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">URL รูปภาพสินค้า</label> <input type="url" name="image_url" value="https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600" required style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"> </div> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.85rem; margin-bottom:0.85rem;"> <div> <label style="font-size:0.85rem; color:var(--teal-accent); font-weight:700; display:block; margin-bottom:4px;"><i class="ph ph-palette"></i> ตัวเลือกสี (คั่นด้วยจุลภาค ,)</label> <input type="text" name="colors" placeholder="เช่น ดำ Obsidian, ขาว Silver" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"> </div> <div> <label style="font-size:0.85rem; color:var(--teal-accent); font-weight:700; display:block; margin-bottom:4px;"><i class="ph ph-ruler"></i> ตัวเลือกไซส์/รุ่น (คั่นด้วยจุลภาค ,)</label> <input type="text" name="sizes" placeholder="เช่น S, M, L, XL" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"> </div> </div> <div style="margin-bottom:1.25rem;"> <label style="font-size:0.85rem; color:var(--text-muted); display:block; margin-bottom:4px;">คำอธิบายสินค้า</label> <textarea name="description" rows="3" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.6rem; border-radius:8px; color:#FFF;"></textarea> </div> <button type="submit" class="btn-primary" style="width:100%;">บันทึกและลงขายสินค้า</button> </form> </div> `; modal.classList.add('open'); document.getElementById('addProductForm')?.addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(e.target); const res = await fetch('api/seller_products.php?action=add', { method: 'POST', body: formData }); const data = await res.json(); if (data.status === 'success') { modal.classList.remove('open'); renderSellerProducts(); } }); }; // 2.4 Inventory Intelligence function renderSellerInventoryAlerts() { container.innerHTML = ` <div style="margin-bottom:1.5rem;"> <h1 style="font-size:1.8rem; font-weight:800;">ระบบวิเคราะห์สต็อกอัจฉริยะ (Inventory Intelligence)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">วิเคราะห์แนวโน้มสินค้าใกล้หมดและความต้องการซื้อ</p> </div> <div style="display:flex; flex-direction:column; gap:1rem;"> <div class="glass-panel" style="padding:1.25rem; border-left:4px solid var(--rose-accent);"> <h4 style="color:var(--rose-accent); font-weight:800; font-size:1rem;"><i class="ph ph-warning"></i> สินค้าความเสี่ยงสต็อกหมดสูง (Stock Depletion Alert)</h4> <p style="font-size:0.85rem; margin-top:0.3rem;">⚠️ <strong>Cyberpunk Oversized Hoodie:</strong> เหลือเพียง 12 ชิ้น (ความต้องการซื้อพุ่งขึ้น +32%) คาดการณ์ว่าสต็อกจะหมดใน 4 วัน แนะนำสั่งเพิ่ม 50 ชิ้น</p> </div> <div class="glass-panel" style="padding:1.25rem; border-left:4px solid var(--emerald-accent);"> <h4 style="color:var(--emerald-accent); font-weight:800; font-size:1rem;"><i class="ph ph-check-circle"></i> สินค้าขายดีต่อเนื่อง (Top Selling Demand)</h4> <p style="font-size:0.85rem; margin-top:0.3rem;">📈 <strong>NEXUS Pro ANC Headphones:</strong> ขายแล้ว 840 ชิ้น สต็อกคงเหลือ 38 ชิ้น เพียงพอสำหรับ 12 วันถัดไป</p> </div> </div> `; } // 2.5 AI Copywriter Tools function renderSellerAITools() { container.innerHTML = ` <div style="margin-bottom:1.5rem;"> <h1 style="font-size:1.8rem; font-weight:800;">AI Seller Assistant & Copywriter</h1> <p style="color:var(--text-muted); font-size:0.9rem;">สร้างคำอธิบายสินค้าและแท็ก SEO อัตโนมัติด้วย AI</p> </div> <div class="glass-panel" style="padding:1.5rem; max-width:700px;"> <h3 style="font-size:1.1rem; font-weight:800; margin-bottom:1rem;"><i class="ph ph-sparkle" style="color:var(--teal-accent);"></i> AI ช่วยเขียนคำอธิบายสินค้า</h3> <input type="text" id="sellerAiTitle" placeholder="ใส่ชื่อสินค้า เช่น หูฟังไร้สาย NEXUS Pro" style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); padding:0.8rem; border-radius:10px; color:#FFF; margin-bottom:1rem;"> <button class="btn-primary" style="width:100%;" onclick="window.nexusSellerAiGenerate()">สร้างคำอธิบายการขายด้วย AI</button> <div id="aiOutBox" style="margin-top:1.5rem;"></div> </div> `; } window.nexusSellerAiGenerate = async function() { const title = document.getElementById('sellerAiTitle')?.value; const box = document.getElementById('aiOutBox'); if (!title || !box) return; box.innerHTML = `<p style="font-size:0.85rem; color:var(--teal-accent);">กำลังประมวลผลคำอธิบายด้วย AI...</p>`; const res = await fetch('api/seller_ai.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'generate_desc', title: title }) }); const data = await res.json(); if (data.status === 'success') { box.innerHTML = ` <div style="background:rgba(255,255,255,0.04); border:1px solid var(--border-glass); border-radius:12px; padding:1rem; white-space:pre-wrap; font-size:0.85rem;"> ${data.generated_description} <div style="margin-top:1rem; color:var(--teal-accent);"> <strong>SEO Keywords:</strong> ${(data.seo_keywords || []).join(' ')} </div> </div> `; } }; // 2.6 Marketing Vouchers function renderSellerVouchers() { container.innerHTML = ` <div style="margin-bottom:1.5rem;"> <h1 style="font-size:1.8rem; font-weight:800;">ศูนย์การตลาดและส่วนลด (Marketing Center)</h1> <p style="color:var(--text-muted); font-size:0.9rem;">สร้างและจัดการคูปองส่วนลดร้านค้าและแคมเปญส่งเสริมการขาย</p> </div> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:1.5rem;"> <div class="glass-panel" style="padding:1.5rem;"> <h3 style="font-size:1.1rem; font-weight:800; margin-bottom:0.5rem;"><i class="ph ph-ticket"></i> โค้ดส่วนลดร้านค้า (Store Voucher)</h3> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1rem;">โค้ดส่วนลด SHOWPIPI-CYBER-100 (ส่วนลด ฿100 เมื่อซื้อครบ ฿1,500)</p> <span style="font-size:0.8rem; background:rgba(16,185,129,0.2); color:var(--emerald-accent); padding:4px 10px; border-radius:12px; font-weight:bold;">กำลังใช้งาน</span> </div> <div class="glass-panel" style="padding:1.5rem;"> <h3 style="font-size:1.1rem; font-weight:800; margin-bottom:0.5rem;"><i class="ph ph-lightning"></i> Mystery Deal Campaign</h3> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1rem;">กล่องสุ่มส่วนลด 10% - 50% สำหรับสินค้าร่วมรายการ</p> <span style="font-size:0.8rem; background:rgba(245,158,11,0.2); color:var(--gold-accent); padding:4px 10px; border-radius:12px; font-weight:bold;">แคมเปญเปิดอยู่</span> </div> </div> `; } function numberFormat(num) { return new Intl.NumberFormat().format(num || 0); } }); window.nexusLogoutAdmin = async function() { if (confirm('ยืนยันออกจากระบบหลังบ้านหรือไม่?')) { await fetch('api/login.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'logout' }) }); window.location.href = 'admin_login.php'; } }; </script> </body> </html>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.25 |
proxy
|
phpinfo
|
Settings