File manager - Edit - /home/webapp69.cm.in.th/u69319090040/Shop/assets/js/app.js
Back
// NEXUS Commerce - Reactive Frontend Master Application Logic document.addEventListener('DOMContentLoaded', () => { // Global State const state = { role: 'customer', // customer, seller, admin currentView: 'home', // home, collections, live, group_buy, quests, seller_dash, admin_dash products: [], categories: [], cart: [], coins: 250, xp: 450, level: 'Insider', quests: [], priceAlerts: [], activeCategory: 'all', activeProduct: null, compareList: [], mysteryCoupon: null, searchFilter: '', toastTimer: null }; // DOM Elements const appContainer = document.getElementById('app'); const navRoleBtns = document.querySelectorAll('.role-btn'); const cartCountBadge = document.getElementById('cartCount'); const userCoinsBadge = document.getElementById('userCoins'); const userTierPill = document.getElementById('userTierPill'); const modalOverlay = document.getElementById('modalOverlay'); const modalBody = document.getElementById('modalBody'); // Initialize App init(); async function init() { setupEventListeners(); await seedDatabase(); await fetchProducts(); await fetchQuests(); try { const savedCart = JSON.parse(localStorage.getItem('nexus_cart') || '[]'); if (Array.isArray(savedCart) && savedCart.length > 0) { state.cart = savedCart; } } catch(e) {} renderMainView(); const urlParams = new URLSearchParams(window.location.search); if (urlParams.get('open_cart') === '1') { openCartModal(); } } async function seedDatabase() { try { await fetch('api/seed.php'); } catch (e) { console.warn('DB seed auto-run', e); } } const defaultFallbackProducts = [ { id: 1, store_id: 1, store_name: 'CyberPulse Official', category_id: 1, category_name: 'Tech & Gadgets', title: 'หูฟังไร้สาย NEXUS Pro Wireless ANC Headphones - Obsidian Black', description: 'หูฟังไร้สายระบบ Active Noise Cancelling แบตเตอรี่อึด 50 ชั่วโมง', price: 2490.00, original_price: 3290.00, stock: 38, sku: 'NEX-AUD-01', trust_score: 97, sales_count: 840, rating_avg: 4.9, is_group_buy: 1, group_price: 1990.00, image_url: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600', gallery: ['https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600'], specs: { 'Battery': '50 Hours', 'Bluetooth': 'v5.3' }, ai_summary: { verdict: 'หูฟัง ANC คุ้มค่าที่สุด', best_for: 'เดินทางลุยยาวนาน' } }, { id: 2, store_id: 1, store_name: 'CyberPulse Official', category_id: 5, category_name: 'Gaming Gear', title: 'คีย์บอร์ดเกมมิ่ง CyberDeck V1 Mechanical RGB Keyboard', description: 'คีย์บอร์ดแมคคานิคอล Gasket mounted สวิตช์ลื่นพรีเมียม', price: 1890.00, original_price: 2450.00, stock: 18, sku: 'NEX-KB-02', trust_score: 95, sales_count: 1250, rating_avg: 4.8, is_group_buy: 1, group_price: 1490.00, image_url: 'https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=600', gallery: ['https://images.unsplash.com/photo-1587829741301-dc798b83add3?w=600'], specs: { 'Switches': 'Linear Yellow', 'Keycaps': 'PBT' }, ai_summary: { verdict: 'ฟีลพิมพ์นุ่มเสียงทุ้มแน่น', best_for: 'สายเกมมิ่งและเขียนโค้ด' } }, { id: 3, store_id: 2, store_name: 'Aura Audio Lab', category_id: 1, category_name: 'Tech & Gadgets', title: 'ไฟแขวนจอคอม Aura LightBar Pro - พร้อมปุ่มหมุนไร้สาย', description: 'โคมไฟแขวนจอคอมพิวเตอร์ ลดแสงสะท้อนถนอมสายตา', price: 1290.00, original_price: 1690.00, stock: 50, sku: 'NEX-LGT-03', trust_score: 98, sales_count: 2100, rating_avg: 4.95, is_group_buy: 0, group_price: 1290.00, image_url: 'https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=600', gallery: ['https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=600'], specs: { 'CRI': 'Ra > 95', 'Color Temp': '2700K - 6500K' }, ai_summary: { verdict: 'โคมไฟถนอมสายตาที่สายทำงานต้องมี', best_for: 'คนทำงานดึกและจัดโต๊ะคอม' } }, { id: 4, store_id: 1, store_name: 'CyberPulse Official', category_id: 2, category_name: 'Cyber Fashion', title: 'เสื้อฮู้ดดี้สตรีทแวร์ Cyberpunk Oversized Hoodie - Neon Purple', description: 'เสื้อฮู้ดผ้าหนา 450gsm ลายสกรีนสะท้อนแสงสไตล์ไซเบอร์', price: 1490.00, original_price: 1990.00, stock: 12, sku: 'NEX-CLO-04', trust_score: 93, sales_count: 630, rating_avg: 4.75, is_group_buy: 1, group_price: 1190.00, image_url: 'https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=600', gallery: ['https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=600'], specs: { 'Material': 'Cotton 80%', 'Weight': '450 GSM' }, ai_summary: { verdict: 'เสื้อฮู้ดทรงโอเวอร์ไซส์ผ้าหนานุ่ม', best_for: 'แฟชั่นสตรีทแวร์' } }, { id: 5, store_id: 2, store_name: 'Aura Audio Lab', category_id: 3, category_name: 'Smart Home', title: 'กระติกน้ำอัจฉริยะ Smart Hydro Flask V2 - หน้าจอ OLED บอกอุณหภูมิ', description: 'กระบอกน้ำเก็บความเย็น 24 ชม. พร้อมระบบฆ่าเชื้อ UV-C ในตัว', price: 890.00, original_price: 1200.00, stock: 65, sku: 'NEX-BOT-05', trust_score: 96, sales_count: 3400, rating_avg: 4.9, is_group_buy: 1, group_price: 690.00, image_url: 'https://images.unsplash.com/photo-1602143407151-7111542de6e8?w=600', gallery: ['https://images.unsplash.com/photo-1602143407151-7111542de6e8?w=600'], specs: { 'Capacity': '650ml', 'Sterilization': 'UV-C LED 99.9%' }, ai_summary: { verdict: 'แก้วน้ำอัจฉริยะฆ่าเชื้อในตัว', best_for: 'ออกกำลังกายและออฟฟิศ' } } ]; async function fetchProducts(category = 'all', search = '') { try { let url = `api/products.php?category=${encodeURIComponent(category)}`; if (search) url += `&search=${encodeURIComponent(search)}`; const res = await fetch(url); const data = await res.json(); if (data.status === 'success' && data.data && data.data.length > 0) { state.products = data.data; } else { state.products = defaultFallbackProducts; } } catch (e) { console.error('Failed to fetch products', e); state.products = defaultFallbackProducts; } } async function fetchQuests() { // Simulated quest dataset state.quests = [ { id: 1, title: 'Daily Attendance Check-In', reward_coins: 15, reward_xp: 50, completed: true }, { id: 2, title: 'Try Interactive 360 Product Room', reward_coins: 30, reward_xp: 100, completed: false }, { id: 3, title: 'Use AI Magic Search Prompt', reward_coins: 25, reward_xp: 80, completed: false }, { id: 4, title: 'Scratch a Mystery Box Deal', reward_coins: 50, reward_xp: 150, completed: false } ]; } function setupEventListeners() { // Role switcher navRoleBtns.forEach(btn => { btn.addEventListener('click', (e) => { navRoleBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); state.role = btn.dataset.role; showToast(`Switched view to ${state.role.toUpperCase()} mode`); if (state.role === 'seller') { state.currentView = 'seller_dash'; } else if (state.role === 'admin') { state.currentView = 'admin_dash'; } else { state.currentView = 'home'; } renderMainView(); }); }); // Magic Search Trigger document.getElementById('searchTrigger')?.addEventListener('click', () => { openMagicSearchModal(); }); // Visual Search Trigger document.getElementById('visualSearchBtn')?.addEventListener('click', () => { openVisualSearchModal(); }); // Cart Modal Trigger document.getElementById('cartBtn')?.addEventListener('click', () => { openCartModal(); }); // Quests & Coin Shop Trigger document.getElementById('questsBtn')?.addEventListener('click', () => { state.currentView = 'quests'; renderMainView(); }); } // Toast System function showToast(message, icon = 'ph-sparkle') { let container = document.getElementById('toastContainer'); if (!container) { container = document.createElement('div'); container.id = 'toastContainer'; container.className = 'toast-container'; document.body.appendChild(container); } const toast = document.createElement('div'); toast.className = 'toast'; toast.innerHTML = `<i class="ph ${icon}"></i> <span>${message}</span>`; container.appendChild(toast); setTimeout(() => { toast.remove(); }, 3500); } // Render Core App View function renderMainView() { // Update Header Status if (cartCountBadge) cartCountBadge.textContent = state.cart.reduce((sum, i) => sum + i.qty, 0); if (userCoinsBadge) userCoinsBadge.textContent = state.coins; if (userTierPill) userTierPill.innerHTML = `<i class="ph ph-crown"></i> ${state.level} Tier`; if (state.role === 'seller') { renderSellerDashboard(); return; } if (state.role === 'admin') { renderAdminDashboard(); return; } if (state.currentView === 'quests') { renderQuestsView(); return; } if (state.currentView === 'collections') { renderCollectionsView(); return; } if (state.currentView === 'live') { renderLiveCommerceView(); return; } if (state.currentView === 'group_buy') { renderGroupBuyView(); return; } renderHomeDiscoveryView(); } // 1. Home Discovery Feed (Thai Version) function renderHomeDiscoveryView() { appContainer.innerHTML = ` <!-- Hero Banner --> <section class="feed-hero"> <div class="hero-banner"> <div> <div style="display:inline-flex; align-items:center; gap:6px; background:rgba(124,58,237,0.15); border:1px solid var(--primary-purple); color:var(--teal-accent); padding:4px 12px; border-radius:20px; font-size:0.8rem; font-weight:700; margin-bottom:1rem;"> <i class="ph ph-sparkle"></i> ระบบแนะนำสินค้าเฉพาะบุคคลด้วย AI </div> <h1 class="hero-title">ค้นพบสินค้าใหม่ <br><span>ที่ตอบโจทย์ไลฟ์สไตล์ของคุณ</span></h1> <p class="hero-subtitle">ประสบการณ์ซื้อขายยุคใหม่ 360° Interactive Product Room, คะแนนความน่าเชื่อถือ Trust Score, ระบบผู้ช่วย AI Personal Shopper และซื้อด้วยกันถูกกว่า (Group Buy)</p> <div class="hero-cta-group"> <button class="btn-primary" id="heroSearchBtn"> <i class="ph ph-magic-wand"></i> ค้นหาด้วยภาษาธรรมชาติ (Magic AI) </button> <button class="btn-glass" id="heroMysteryBtn"> <i class="ph ph-gift"></i> เปิดกล่องสุ่มส่วนลด (Mystery Box) </button> <button class="btn-glass" id="heroLiveBtn"> <i class="ph ph-broadcast"></i> รับชมถ่ายทอดสด (Live Shopping) </button> </div> </div> <div style="position:relative; text-align:center;"> <div class="glass-panel" style="padding:1.5rem; text-align:left;"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">สินค้าแนะนำประจำวันโดย AI</span> <span class="trust-badge-pill"><i class="ph ph-shield-check"></i> คะแนนความน่าเชื่อถือ 97/100</span> </div> <img src="${state.products[0]?.image_url || 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=600'}" style="width:100%; height:180px; object-fit:cover; border-radius:12px; margin-bottom:1rem;"> <h3 style="font-size:1.1rem; font-weight:700;">${state.products[0]?.title || 'หูฟังไร้สาย NEXUS Pro ANC'}</h3> <div style="display:flex; justify-content:space-between; align-items:center; margin-top:0.5rem;"> <span class="current-price">฿${numberFormat(state.products[0]?.price || 2490)}</span> <button class="btn-card-primary" style="padding:0.4rem 1rem;" onclick="window.nexusOpenRoom(1)">ทดลองดูหมุน 360°</button> </div> </div> </div> </div> </section> <!-- Product Cards Grid --> <section class="product-grid"> ${state.products.map(p => ` <div class="glass-card product-card"> <div class="product-thumb-wrap" onclick="location.href='product.php?id=${p.id}'" style="cursor:pointer;"> <img src="${p.image_url}" class="product-thumb" alt="${p.title}"> <div class="trust-badge-pill"> <i class="ph ph-shield-check"></i> ความน่าเชื่อถือ ${p.trust_score}/100 </div> ${p.is_group_buy ? `<div class="groupbuy-tag">ราคาซื้อกลุ่ม ฿${numberFormat(p.group_price)}</div>` : ''} </div> <div class="product-details"> <div class="product-ai-reason"> <i class="ph ph-sparkle"></i> ${p.ai_summary?.best_for ? 'เหมาะสำหรับ: ' + p.ai_summary.best_for : 'แนะนำตรงตามความสนใจของคุณ'} </div> <h3 class="product-title" onclick="location.href='product.php?id=${p.id}'" style="cursor:pointer;" title="คลิกเพื่อดูรายละเอียดสินค้าฉบับเต็ม">${p.title}</h3> <div class="price-row"> <span class="current-price">฿${numberFormat(p.price)}</span> ${p.original_price ? `<span class="original-price">฿${numberFormat(p.original_price)}</span>` : ''} </div> <div class="card-action-bar"> <button class="btn-card-primary" onclick="location.href='product.php?id=${p.id}'"> <i class="ph ph-eye"></i> ดูรายละเอียดสินค้า </button> <button class="icon-btn" onclick="window.nexusQuickAdd(${p.id})" title="ใส่ตะกร้า"> <i class="ph ph-shopping-bag"></i> </button> <button class="icon-btn" onclick="window.nexusToggleAlert(${p.id})" title="จับตาราคาลด"> <i class="ph ph-bell"></i> </button> </div> </div> </div> `).join('')} </section> `; document.getElementById('heroSearchBtn')?.addEventListener('click', openMagicSearchModal); document.getElementById('heroMysteryBtn')?.addEventListener('click', openMysteryModal); document.getElementById('heroLiveBtn')?.addEventListener('click', () => { state.currentView = 'live'; renderMainView(); }); } // 2. Interactive Product Room (360 Rotator, Trust Score Gauge, Complete Setup, Help Me Decide) window.nexusOpenRoom = async function(productId) { try { const res = await fetch(`api/products.php?id=${productId}`); const data = await res.json(); if (data.status !== 'success') return; const p = data.data; state.activeProduct = p; openModal(` <div style="max-width:650px; margin:0 auto;"> <!-- Right Column: Specs, AI Assistant & Actions --> <div> <span style="font-size:0.8rem; color:var(--teal-accent); font-weight:700;">ร้านค้า: ${p.store_name}</span> <h2 style="font-size:1.6rem; font-weight:800; margin:0.3rem 0 0.8rem 0;">${p.title}</h2> <div style="display:flex; align-items:baseline; gap:1rem; margin-bottom:1rem;"> <span class="current-price" style="font-size:2rem;">฿${numberFormat(p.price)}</span> ${p.original_price ? `<span class="original-price" style="font-size:1.1rem;">฿${numberFormat(p.original_price)}</span>` : ''} </div> <!-- AI Summary Box --> <div class="glass-panel" style="padding:1rem; margin-bottom:1rem; border-left:3px solid var(--primary-purple);"> <h4 style="font-size:0.85rem; font-weight:700; color:var(--teal-accent); display:flex; align-items:center; gap:6px;"> <i class="ph ph-sparkle"></i> ข้อสรุปคำแนะนำจาก AI (AI Verdict) </h4> <p style="font-size:0.85rem; margin-top:0.4rem; color:var(--text-main);">${p.ai_summary?.verdict || 'สินค้าคุ้มค่า คุ้มราคา การันตีคุณภาพวัสดุและการใช้งาน'}</p> </div> <!-- Full Description Box --> <h4 style="font-size:0.9rem; font-weight:700; margin-bottom:0.4rem;">รายละเอียดสินค้า (Product Details)</h4> <div style="background:rgba(255,255,255,0.03); border:1px solid var(--border-glass); padding:0.85rem; border-radius:10px; font-size:0.82rem; color:var(--text-muted); line-height:1.6; white-space:pre-line; max-height:140px; overflow-y:auto; margin-bottom:1rem;"> ${p.description || 'ไม่มีรายละเอียดเพิ่มเติม'} </div> <!-- Specs Grid --> <h4 style="font-size:0.9rem; font-weight:700; margin-bottom:0.5rem;">คุณสมบัติเด่น (Specifications)</h4> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:0.5rem; font-size:0.8rem; margin-bottom:1.5rem;"> ${Object.entries(p.specs || {}).map(([k, v]) => ` <div style="background:rgba(255,255,255,0.04); padding:6px 10px; border-radius:6px;"> <span style="color:var(--text-muted);">${k}:</span> <strong>${v}</strong> </div> `).join('')} </div> <!-- Action Buttons --> <div style="display:flex; gap:0.75rem; flex-wrap:wrap;"> <button class="btn-primary" style="flex:1;" onclick="window.nexusAddToCart(${p.id})"> <i class="ph ph-shopping-cart"></i> ใส่ตะกร้าสินค้า </button> <a href="product.php?id=${p.id}" class="btn-glass" style="text-decoration:none; display:inline-flex; align-items:center; gap:6px;"> <i class="ph ph-arrow-square-out"></i> ดูหน้าสเปกฉบับเต็ม </a> <button class="btn-glass" onclick="window.nexusHelpMeDecide(${p.id})"> <i class="ph ph-brain"></i> ช่วยตัดสินใจ </button> </div> </div> </div> `); // 360 Rotator Drag Logic const rotatorContainer = document.getElementById('rotator360'); const rotatorImg = document.getElementById('rotatorImg'); let isDragging = false; let startX = 0; let rotateDeg = 0; rotatorContainer?.addEventListener('mousedown', (e) => { isDragging = true; startX = e.clientX; }); window.addEventListener('mouseup', () => { isDragging = false; }); rotatorContainer?.addEventListener('mousemove', (e) => { if (!isDragging) return; const delta = e.clientX - startX; rotateDeg += delta * 0.5; startX = e.clientX; rotatorImg.style.transform = `rotateY(${rotateDeg}deg) scale(1.05)`; }); } catch (e) { console.error(e); } }; // AI "Help Me Decide" Decision Assistant Modal window.nexusHelpMeDecide = function(productId) { const p = state.activeProduct; if (!p) return; openModal(` <div style="text-align:center; max-width:600px; margin:0 auto;"> <i class="ph ph-brain" style="font-size:3rem; color:var(--teal-accent);"></i> <h2 style="font-size:1.5rem; font-weight:800; margin:0.5rem 0;">AI Decision Assistant</h2> <p style="color:var(--text-muted); font-size:0.9rem; margin-bottom:1.5rem;">Analyzing product suitability based on user ratings and complaint history</p> <div style="display:grid; grid-template-columns: 1fr 1fr; gap:1rem; text-align:left; margin-bottom:1.5rem;"> <div style="background:rgba(16,185,129,0.1); border:1px solid rgba(16,185,129,0.3); padding:1rem; border-radius:12px;"> <h4 style="color:var(--emerald-accent); font-size:0.9rem; font-weight:700; margin-bottom:0.5rem;"><i class="ph ph-thumbs-up"></i> Why You Should Buy</h4> <ul style="font-size:0.8rem; padding-left:1.2rem; color:var(--text-main);"> ${(p.ai_summary?.pros || ['High build quality', 'Excellent battery life']).map(item => `<li>${item}</li>`).join('')} </ul> </div> <div style="background:rgba(244,63,94,0.1); border:1px solid rgba(244,63,94,0.3); padding:1rem; border-radius:12px;"> <h4 style="color:var(--rose-accent); font-size:0.9rem; font-weight:700; margin-bottom:0.5rem;"><i class="ph ph-thumbs-down"></i> Potential Considerations</h4> <ul style="font-size:0.8rem; padding-left:1.2rem; color:var(--text-main);"> ${(p.ai_summary?.cons || ['Carrying case size']).map(item => `<li>${item}</li>`).join('')} </ul> </div> </div> <button class="btn-primary" onclick="closeModal()">Got it, back to product</button> </div> `); }; // 3. Magic AI Natural Language Search Modal function openMagicSearchModal() { openModal(` <div style="max-width:700px; margin:0 auto;"> <div style="display:flex; align-items:center; gap:0.75rem; margin-bottom:1rem;"> <div class="brand-icon"><i class="ph ph-sparkle"></i></div> <div> <h2 style="font-size:1.4rem; font-weight:800;">Magic AI Search</h2> <p style="font-size:0.85rem; color:var(--text-muted);">Describe what you need in natural Thai or English</p> </div> </div> <div style="position:relative; margin-bottom:1.5rem;"> <input type="text" id="magicSearchInput" placeholder='Try: "หารองเท้าสีดำสำหรับผู้ชาย งบไม่เกิน 2,000 ใส่ทำงานได้และเดินเยอะ"' style="width:100%; background:rgba(0,0,0,0.5); border:1px solid var(--primary-purple); border-radius:30px; padding:0.9rem 3.5rem 0.9rem 1.2rem; color:#FFF; font-size:0.95rem; outline:none;"> <button class="btn-primary" id="magicSearchExecBtn" style="position:absolute; right:6px; top:6px; padding:0.5rem 1.2rem; border-radius:24px;">Search</button> </div> <div id="magicSearchResults"> <div style="text-align:center; padding:2rem; color:var(--text-subtle);"> <i class="ph ph-brain" style="font-size:2.5rem; margin-bottom:0.5rem;"></i> <p>Enter a query above to see AI reasoning breakdowns</p> </div> </div> </div> `); document.getElementById('magicSearchExecBtn')?.addEventListener('click', runMagicSearch); document.getElementById('magicSearchInput')?.addEventListener('keypress', (e) => { if (e.key === 'Enter') runMagicSearch(); }); } async function runMagicSearch() { const input = document.getElementById('magicSearchInput')?.value; const resultsContainer = document.getElementById('magicSearchResults'); if (!input || !resultsContainer) return; resultsContainer.innerHTML = ` <div style="text-align:center; padding:2rem;"> <i class="ph ph-arrows-clockwiseSpin" style="font-size:2rem; color:var(--teal-accent); animation:spin 1s linear infinite;"></i> <p style="margin-top:0.5rem; font-size:0.85rem;">AI is analyzing price constraints, intent & specs...</p> </div> `; try { const res = await fetch('api/magic_search.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: input }) }); const data = await res.json(); if (data.status === 'success') { resultsContainer.innerHTML = ` <div style="background:rgba(124,58,237,0.1); border:1px solid var(--primary-purple); border-radius:12px; padding:0.85rem; margin-bottom:1rem; font-size:0.85rem;"> <i class="ph ph-sparkle" style="color:var(--teal-accent);"></i> <strong>AI Prompt Intent:</strong> ${data.ai_analysis.perceived_intent} </div> <div style="display:flex; flex-direction:column; gap:0.75rem;"> ${data.data.map(p => ` <div class="glass-card" style="padding:1rem; display:flex; gap:1rem; align-items:center;"> <img src="${p.image_url}" style="width:70px; height:70px; object-fit:cover; border-radius:8px;"> <div style="flex-grow:1;"> <h4 style="font-size:0.95rem; font-weight:700;">${p.title}</h4> <div style="font-size:0.78rem; color:var(--teal-accent); margin:0.2rem 0;">${p.ai_reasoning}</div> <span class="current-price" style="font-size:1.1rem;">฿${numberFormat(p.price)}</span> </div> <button class="btn-card-primary" onclick="closeModal(); window.nexusOpenRoom(${p.id});">View Product</button> </div> `).join('')} </div> `; } } catch (e) { resultsContainer.innerHTML = `<p style="color:var(--rose-accent);">Failed to search</p>`; } } // 4. Visual Search Modal function openVisualSearchModal() { openModal(` <div style="max-width:650px; margin:0 auto; text-align:center;"> <i class="ph ph-camera" style="font-size:2.5rem; color:var(--teal-accent); margin-bottom:0.5rem;"></i> <h2 style="font-size:1.4rem; font-weight:800;">Visual AI Search</h2> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1.5rem;">Upload or drop an image to search visually similar products</p> <div style="border:2px dashed var(--border-glass); padding:2.5rem; border-radius:16px; background:rgba(0,0,0,0.3); margin-bottom:1.5rem; cursor:pointer;" id="dropZone" onclick="window.nexusSimulateVisualUpload()"> <i class="ph ph-cloud-arrow-up" style="font-size:2rem; color:var(--primary-purple);"></i> <p style="font-size:0.9rem; font-weight:600; margin-top:0.5rem;">Click to simulate uploading product photo</p> <span style="font-size:0.75rem; color:var(--text-subtle);">Supports PNG, JPG, WEBP</span> </div> <div id="visualScanResults"></div> </div> `); } window.nexusSimulateVisualUpload = async function() { const resBox = document.getElementById('visualScanResults'); if (!resBox) return; resBox.innerHTML = ` <div style="padding:1.5rem;"> <i class="ph ph-scan" style="font-size:2rem; color:var(--teal-accent); animation:pulse 1s infinite;"></i> <p style="margin-top:0.5rem; font-size:0.85rem;">Scanning image features & matching catalog...</p> </div> `; try { const res = await fetch('api/visual_search.php'); const data = await res.json(); if (data.status === 'success') { resBox.innerHTML = ` <div style="text-align:left; background:rgba(6,182,212,0.1); border:1px solid rgba(6,182,212,0.3); border-radius:12px; padding:0.85rem; margin-bottom:1rem; font-size:0.8rem;"> <strong>Detected:</strong> ${data.image_detected.object_type} • Est. Value: ${data.image_detected.estimated_market_val} </div> <div style="display:flex; flex-direction:column; gap:0.75rem; text-align:left;"> ${data.data.map(p => ` <div class="glass-card" style="padding:0.85rem; display:flex; align-items:center; gap:1rem;"> <img src="${p.image_url}" style="width:60px; height:60px; object-fit:cover; border-radius:8px;"> <div style="flex-grow:1;"> <span style="font-size:0.75rem; background:rgba(16,185,129,0.2); color:var(--emerald-accent); padding:2px 6px; border-radius:4px; font-weight:700;">${p.similarity_score}% Visual Match</span> <h4 style="font-size:0.9rem; font-weight:700; margin-top:3px;">${p.title}</h4> <span class="current-price" style="font-size:1rem;">฿${numberFormat(p.price)}</span> </div> <button class="btn-card-primary" onclick="closeModal(); window.nexusOpenRoom(${p.id});">View</button> </div> `).join('')} </div> `; } } catch (e) { console.error(e); } }; // 5. Mystery Deal Reveal Box function openMysteryModal() { openModal(` <div style="max-width:500px; margin:0 auto; text-align:center;"> <i class="ph ph-gift" style="font-size:3rem; color:var(--gold-accent); margin-bottom:0.5rem;"></i> <h2 style="font-size:1.6rem; font-weight:800;">Flash Mystery Box</h2> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1.5rem;">Click the mystery box below to reveal your random discount coupon!</p> <div class="scratch-box" id="scratchCard" onclick="window.nexusRevealMystery()"> <i class="ph ph-question" style="font-size:4rem; color:#FFF;"></i> <h3 style="font-size:1.2rem; font-weight:800; margin-top:0.5rem; color:#FFF;">REVEAL MYSTERY DISCOUNT</h3> </div> <div id="mysteryResult" style="margin-top:1.5rem;"></div> </div> `); } window.nexusRevealMystery = function() { const resultBox = document.getElementById('mysteryResult'); const discounts = [10, 20, 30, 50]; const discount = discounts[Math.floor(Math.random() * discounts.length)]; state.mysteryCoupon = discount; resultBox.innerHTML = ` <div style="background:var(--gradient-gold); color:#000; padding:1.5rem; border-radius:16px; font-weight:800; font-family:'Outfit', sans-serif;"> <h2 style="font-size:2.2rem; font-weight:900;">YOU WON ${discount}% OFF!</h2> <p style="font-size:0.9rem;">Coupon CODE: <strong>NEXUS-MYSTERY-${discount}</strong></p> <p style="font-size:0.75rem; margin-top:0.5rem;">Automatically applied to your next Smart Cart checkout!</p> </div> `; showToast(`Unlocked ${discount}% Mystery Discount Coupon!`, 'ph-gift'); }; // 6. Social Shopping & Setup Collections function renderCollectionsView() { appContainer.innerHTML = ` <section style="max-width:1200px; margin:2rem auto; padding:0 2rem;"> <h1 style="font-size:2rem; font-weight:800; margin-bottom:0.5rem;">Community & Creator Collections</h1> <p style="color:var(--text-muted); margin-bottom:2rem;">Explore curated desk setups and fashion styles. Buy the entire setup with a single click.</p> <div style="display:grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap:2rem;"> <div class="glass-panel" style="padding:1.5rem;"> <img src="https://images.unsplash.com/photo-1527443224154-c4a3942d3acf?w=800" style="width:100%; height:200px; object-fit:cover; border-radius:14px; margin-bottom:1rem;"> <span style="font-size:0.75rem; color:var(--teal-accent); font-weight:700;">CURATED BY: JOHN CREATOR</span> <h3 style="font-size:1.2rem; font-weight:800; margin:0.3rem 0;">Minimalist Cyber Workstation 2026</h3> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1rem;">Complete clean workspace including monitor lightbar, wireless mechanical keyboard, and ANC headphones.</p> <div style="display:flex; justify-content:space-between; align-items:center;"> <span class="current-price">Total: ฿5,670</span> <button class="btn-primary" onclick="window.nexusBuyEntireSetup([1, 2, 3])"> <i class="ph ph-shopping-bag-open"></i> Buy Entire Setup </button> </div> </div> <div class="glass-panel" style="padding:1.5rem;"> <img src="https://images.unsplash.com/photo-1556905055-8f358a7a47b2?w=800" style="width:100%; height:200px; object-fit:cover; border-radius:14px; margin-bottom:1rem;"> <span style="font-size:0.75rem; color:var(--teal-accent); font-weight:700;">CURATED BY: ALEX VANCE</span> <h3 style="font-size:1.2rem; font-weight:800; margin:0.3rem 0;">Urban Commuter Techwear</h3> <p style="font-size:0.85rem; color:var(--text-muted); margin-bottom:1rem;">Heavyweight reflective hoodie coupled with smart hydro bottle and noise-canceling audio gear.</p> <div style="display:flex; justify-content:space-between; align-items:center;"> <span class="current-price">Total: ฿4,870</span> <button class="btn-primary" onclick="window.nexusBuyEntireSetup([1, 4, 5])"> <i class="ph ph-shopping-bag-open"></i> Buy Entire Setup </button> </div> </div> </div> </section> `; } window.nexusBuyEntireSetup = function(productIds) { productIds.forEach(id => { const p = state.products.find(item => item.id === id); if (p) { const existing = state.cart.find(c => c.id === p.id); if (existing) existing.qty += 1; else state.cart.push({ ...p, qty: 1 }); } }); showToast('Added entire setup collection to cart with bundle discount!', 'ph-shopping-bag-open'); renderMainView(); openCartModal(); }; // 7. Live Commerce Studio Simulation function renderLiveCommerceView() { appContainer.innerHTML = ` <section style="max-width:1100px; margin:2rem auto; padding:0 2rem;"> <div style="display:grid; grid-template-columns: 1.4fr 0.6fr; gap:1.5rem;"> <!-- Live Stream Screen --> <div class="live-stream-box"> <img src="https://images.unsplash.com/photo-1598899134739-24c46f58b8c0?w=1000" class="live-video-sim"> <div class="live-badge"><i class="ph ph-record"></i> LIVE STREAMING</div> <div class="live-pinned-deal"> <div> <span style="font-size:0.7rem; color:var(--gold-accent); font-weight:800;">PINNED FLASH DEAL • 02:35 LEFT</span> <h4 style="font-size:0.9rem; font-weight:700; color:#FFF;">NEXUS Pro ANC Headphones</h4> <span class="current-price" style="font-size:1.1rem;">฿1,990 <span style="font-size:0.75rem; color:var(--text-subtle); text-decoration:line-through;">฿3,290</span></span> </div> <button class="btn-primary" style="padding:0.4rem 1rem;" onclick="window.nexusQuickAdd(1)">Claim Live Deal</button> </div> </div> <!-- Chat & Poll Sidebar --> <div class="glass-panel" style="padding:1.25rem; display:flex; flex-direction:column; height:450px;"> <h3 style="font-size:1rem; font-weight:800; margin-bottom:0.75rem;"><i class="ph ph-chats-circle"></i> Live Community Chat</h3> <div style="flex-grow:1; overflow-y:auto; font-size:0.8rem; display:flex; flex-direction:column; gap:0.5rem;"> <div><strong style="color:var(--teal-accent);">CyberFan99:</strong> หูฟังรุ่นนี้ตัดเสียงเบสแน่นมากครับ!</div> <div><strong style="color:var(--gold-accent);">Sara_K:</strong> กดซื้อทันดีล Live พอดีเลยยย</div> <div><strong style="color:var(--emerald-accent);">TechReviewer:</strong> แบตเตอรี่อึด 50 ชม. ใช้งานจริงได้ยาวๆ</div> </div> <div style="display:flex; gap:0.5rem; margin-top:0.75rem;"> <input type="text" placeholder="Send a reaction..." style="flex-grow:1; background:rgba(0,0,0,0.4); border:1px solid var(--border-glass); border-radius:20px; padding:0.4rem 0.8rem; color:#FFF; font-size:0.8rem;"> <button class="icon-btn" onclick="showToast('❤️ Heart reaction sent!')">❤️</button> </div> </div> </div> </section> `; } // 8. Group Buy Hub function renderGroupBuyView() { const groupProducts = state.products.filter(p => p.is_group_buy); appContainer.innerHTML = ` <section style="max-width:1200px; margin:2rem auto; padding:0 2rem;"> <h1 style="font-size:2rem; font-weight:800; margin-bottom:0.5rem;">Group Buy Hub — "ซื้อด้วยกันถูกกว่า"</h1> <p style="color:var(--text-muted); margin-bottom:2rem;">Team up with friends or auto-match with active shoppers to unlock exclusive group tiers.</p> <div style="display:grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap:1.5rem;"> ${groupProducts.map(p => ` <div class="glass-card" style="padding:1.25rem;"> <img src="${p.image_url}" style="width:100%; height:180px; object-fit:cover; border-radius:12px; margin-bottom:0.85rem;"> <h3 style="font-size:1.1rem; font-weight:700;">${p.title}</h3> <div style="background:rgba(245,158,11,0.1); border:1px solid rgba(245,158,11,0.3); border-radius:10px; padding:0.75rem; margin:0.75rem 0; font-size:0.8rem;"> <div style="display:flex; justify-content:space-between; margin-bottom:0.4rem;"> <span>Solo Price: ฿${numberFormat(p.price)}</span> <strong style="color:var(--gold-accent);">Group Price: ฿${numberFormat(p.group_price)}</strong> </div> <div style="height:6px; background:rgba(255,255,255,0.1); border-radius:3px; overflow:hidden;"> <div style="width:66%; height:100%; background:var(--gold-accent);"></div> </div> <span style="font-size:0.72rem; color:var(--text-muted); display:block; margin-top:4px;">2 of 3 Buyers Joined • 1 Slot Left</span> </div> <button class="btn-primary" style="width:100%;" onclick="window.nexusJoinGroup(${p.id})"> <i class="ph ph-user-plus"></i> Join / Start Group Slot </button> </div> `).join('')} </div> </section> `; } window.nexusJoinGroup = function(productId) { const p = state.products.find(i => i.id === productId); if (!p) return; const existing = state.cart.find(c => c.id === p.id); if (existing) existing.qty += 1; else state.cart.push({ ...p, price: p.group_price, qty: 1 }); showToast(`Unlocked Group Buy Price ฿${numberFormat(p.group_price)}!`, 'ph-users-three'); renderMainView(); openCartModal(); }; // 9. Quests & Gamification View function renderQuestsView() { appContainer.innerHTML = ` <section style="max-width:900px; margin:2rem auto; padding:0 2rem;"> <div class="glass-panel" style="padding:2rem; margin-bottom:2rem; display:flex; justify-content:space-between; align-items:center;"> <div> <span class="user-tier-pill" style="margin-bottom:0.5rem; display:inline-flex;">Personal Level: ${state.level}</span> <h1 style="font-size:2rem; font-weight:800;">Shopping Quests & Rewards</h1> <p style="color:var(--text-muted); font-size:0.9rem;">Complete daily activities to earn NEXUS Coins and level up your shopping perks.</p> </div> <div style="text-align:right;"> <span style="font-size:2.2rem; font-weight:900; color:var(--gold-accent); font-family:'Outfit', sans-serif;">${state.coins}</span> <span style="font-size:0.85rem; color:var(--text-muted); display:block;">NEXUS Coins</span> </div> </div> <h3 style="font-size:1.2rem; font-weight:700; margin-bottom:1rem;">Active Daily Quests</h3> ${state.quests.map(q => ` <div class="quest-item"> <div> <h4 style="font-size:0.95rem; font-weight:700;">${q.title}</h4> <span style="font-size:0.78rem; color:var(--gold-accent);">+${q.reward_coins} Coins • +${q.reward_xp} XP</span> </div> ${q.completed ? ` <span style="font-size:0.8rem; color:var(--emerald-accent); font-weight:700;"><i class="ph ph-check"></i> Completed</span> ` : ` <button class="btn-glass" style="padding:4px 14px; font-size:0.8rem;" onclick="window.nexusClaimQuest(${q.id})">Claim Reward</button> `} </div> `).join('')} </section> `; } window.nexusClaimQuest = function(questId) { const q = state.quests.find(item => item.id === questId); if (q && !q.completed) { q.completed = true; state.coins += q.reward_coins; state.xp += q.reward_xp; showToast(`Claimed +${q.reward_coins} NEXUS Coins!`, 'ph-coins'); renderMainView(); } }; // 10. Smart Cart Modal (Shopee-Grade Interactive Shopping Cart) function openCartModal() { const subtotal = state.cart.reduce((sum, item) => sum + (item.price * item.qty), 0); const freeShipThreshold = 3000; const diff = freeShipThreshold - subtotal; const shippingFee = (subtotal >= freeShipThreshold || subtotal === 0) ? 0 : 50; let discountAmount = 0; if (state.mysteryCoupon) { discountAmount = (subtotal * state.mysteryCoupon) / 100; } else if (state.appliedPromoCode) { discountAmount = state.appliedPromoCode.discount; } let coinsDiscount = state.useCoins ? Math.min(state.coins, subtotal) : 0; const finalTotal = Math.max(0, subtotal - discountAmount - coinsDiscount + shippingFee); openModal(` <div style="max-width:700px; margin:0 auto;"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;"> <h2 style="font-size:1.5rem; font-weight:800; display:flex; align-items:center; gap:8px;"> <i class="ph ph-shopping-bag" style="color:var(--teal-accent);"></i> ตะกร้าสินค้าอัจฉริยะ (Smart Shopping Cart) </h2> <span style="font-size:0.85rem; color:var(--text-muted);">จำนวนทั้งหมด ${state.cart.reduce((sum, i) => sum + i.qty, 0)} ชิ้น</span> </div> <!-- Free Shipping Progress Bar --> <div style="background:rgba(124,58,237,0.12); border:1px solid var(--primary-purple); border-radius:14px; padding:0.85rem 1.1rem; margin-bottom:1.25rem;"> <div style="display:flex; justify-content:space-between; font-size:0.82rem; font-weight:700; margin-bottom:0.4rem;"> <span>${diff <= 0 ? '🎉 ปลดล็อกสิทธิ์จัดส่งฟรีเรียบร้อยแล้ว!' : `ช้อปเพิ่มอีกเพียง ฿${numberFormat(diff)} เพื่อรับสิทธิ์ส่งฟรีทั่วไทย`}</span> <span>฿${numberFormat(subtotal)} / ฿3,000</span> </div> <div style="height:7px; background:rgba(255,255,255,0.1); border-radius:4px; overflow:hidden;"> <div style="width:${Math.min(100, (subtotal / freeShipThreshold) * 100)}%; height:100%; background:var(--gradient-cyber); transition:width 0.3s ease;"></div> </div> </div> ${state.cart.length === 0 ? ` <div style="text-align:center; padding:3.5rem 1rem; color:var(--text-subtle);"> <i class="ph ph-shopping-bag-open" style="font-size:4rem; color:var(--primary-purple); margin-bottom:0.75rem;"></i> <h3 style="font-size:1.2rem; font-weight:700; color:#FFF; margin-bottom:0.4rem;">ยังไม่มีสินค้าในตะกร้า</h3> <p style="font-size:0.88rem; color:var(--text-muted); margin-bottom:1.5rem;">เลือกชมสินค้าคุณภาพจากร้านค้าที่ได้รับการยืนยันแล้วได้ทันที</p> <button class="btn-primary" onclick="closeModal(); state.currentView='home'; renderMainView();"> <i class="ph ph-storefront"></i> เริ่มต้นเลือกชมสินค้า </button> </div> ` : ` <!-- Product Cart List --> <div style="display:flex; flex-direction:column; gap:0.85rem; margin-bottom:1.5rem; max-height:280px; overflow-y:auto; padding-right:4px;"> ${state.cart.map((item, idx) => ` <div class="glass-card" style="padding:0.85rem 1.1rem; display:flex; align-items:center; justify-content:space-between; gap:1rem;"> <div style="display:flex; align-items:center; gap:1rem; flex:1;"> <img src="${item.image_url}" style="width:60px; height:60px; object-fit:cover; border-radius:10px; border:1px solid var(--border-glass);"> <div> <h4 style="font-size:0.92rem; font-weight:700; color:#FFF; margin-bottom:3px;">${item.title}</h4> <span style="font-size:0.78rem; color:var(--teal-accent); display:block; margin-bottom:4px;"> ${item.selectedVariantText || 'ตัวเลือกหลัก / มาตรฐาน'} </span> <strong class="current-price" style="font-size:1.05rem;">฿${numberFormat(item.price)}</strong> </div> </div> <!-- Quantity Modifier --> <div style="display:flex; align-items:center; gap:0.75rem;"> <div class="qty-picker"> <button class="qty-btn" onclick="window.nexusUpdateCartQty(${idx}, -1)">-</button> <input type="text" class="qty-input" value="${item.qty}" readonly style="width:36px; height:32px; font-size:0.85rem;"> <button class="qty-btn" onclick="window.nexusUpdateCartQty(${idx}, 1)">+</button> </div> <button class="icon-btn" style="width:34px; height:34px; color:var(--rose-accent);" onclick="window.nexusRemoveCart(${idx})" title="ลบสินค้า"> <i class="ph ph-trash"></i> </button> </div> </div> `).join('')} </div> <!-- Promo Coupon & Coins Section --> <div style="background:rgba(255,255,255,0.03); border:1px solid var(--border-glass); border-radius:14px; padding:1rem; margin-bottom:1.25rem;"> <!-- Coupon Code Entry --> <div style="display:flex; gap:0.6rem; margin-bottom:0.75rem;"> <input type="text" id="cartPromoInput" placeholder="กรอกโค้ดส่วนลด (เช่น NEXUS100)" value="${state.appliedPromoCode ? state.appliedPromoCode.code : ''}" style="flex:1; background:rgba(0,0,0,0.5); border:1px solid var(--border-glass); border-radius:8px; padding:0.5rem 0.8rem; color:#FFF; font-size:0.85rem; text-transform:uppercase;"> <button class="btn-glass" style="padding:0.5rem 1rem; font-size:0.85rem;" onclick="window.nexusApplyPromoCode()">ใช้โค้ด</button> </div> <!-- Auto Applied Coupon Badge --> ${state.mysteryCoupon ? ` <div style="background:rgba(245,158,11,0.15); border:1px solid var(--gold-accent); padding:0.6rem 0.9rem; border-radius:8px; font-size:0.8rem; color:var(--gold-accent); margin-bottom:0.6rem; display:flex; justify-content:space-between; align-items:center;"> <span><i class="ph ph-gift"></i> 🎁 คูปองกล่องสุ่มส่วนลด ${state.mysteryCoupon}%</span> <strong>-฿${numberFormat(discountAmount)}</strong> </div> ` : ''} <!-- NEXUS Coins Toggle --> <div style="display:flex; justify-content:space-between; align-items:center; font-size:0.85rem;"> <div style="display:flex; align-items:center; gap:8px;"> <input type="checkbox" id="useCoinsCheck" ${state.useCoins ? 'checked' : ''} onchange="window.nexusToggleCoins(this.checked)" style="width:16px; height:16px; cursor:pointer;"> <label for="useCoinsCheck" style="cursor:pointer; color:var(--gold-accent); font-weight:600;"> <i class="ph ph-coins"></i> ใช้ NEXUS Coins สะสม (${state.coins} Coins = ลด ฿${state.coins}) </label> </div> ${state.useCoins ? `<strong style="color:var(--gold-accent);">-฿${numberFormat(coinsDiscount)}</strong>` : ''} </div> </div> <!-- Order Cost Breakdown --> <div style="background:rgba(0,0,0,0.3); border-radius:12px; padding:1rem; margin-bottom:1.5rem; font-size:0.88rem;"> <div style="display:flex; justify-content:space-between; margin-bottom:0.4rem; color:var(--text-muted);"> <span>ยอดรวมสินค้า (Subtotal):</span> <span>฿${numberFormat(subtotal)}</span> </div> ${discountAmount > 0 ? ` <div style="display:flex; justify-content:space-between; margin-bottom:0.4rem; color:var(--gold-accent);"> <span>ส่วนลดคูปอง (Discount):</span> <span>-฿${numberFormat(discountAmount)}</span> </div> ` : ''} ${coinsDiscount > 0 ? ` <div style="display:flex; justify-content:space-between; margin-bottom:0.4rem; color:var(--gold-accent);"> <span>ส่วนลดเหรียญ Coins:</span> <span>-฿${numberFormat(coinsDiscount)}</span> </div> ` : ''} <div style="display:flex; justify-content:space-between; margin-bottom:0.6rem; color:var(--text-muted);"> <span>ค่าจัดส่ง (Shipping Fee):</span> <span>${shippingFee === 0 ? '<strong style="color:var(--emerald-accent);">ฟรี (Free)</strong>' : `฿${shippingFee}`}</span> </div> <div style="border-top:1px dashed var(--border-glass); pt:0.6rem; margin-top:0.5rem; display:flex; justify-content:space-between; align-items:center;"> <span style="font-size:1.05rem; font-weight:800; color:#FFF;">ยอดชำระสุทธิ (Total Net Amount):</span> <span class="current-price" style="font-size:1.6rem;">฿${numberFormat(finalTotal)}</span> </div> </div> <button class="btn-primary" style="width:100%; padding:0.95rem; font-size:1.05rem;" onclick="window.nexusProceedCheckoutPage()"> <i class="ph ph-credit-card"></i> ดำเนินการชำระเงิน (Proceed to Checkout) </button> `} </div> `); } window.nexusUpdateCartQty = function(index, delta) { if (state.cart[index]) { state.cart[index].qty += delta; if (state.cart[index].qty <= 0) { state.cart.splice(index, 1); } } try { localStorage.setItem('nexus_cart', JSON.stringify(state.cart)); } catch(e){} openCartModal(); renderMainView(); }; window.nexusRemoveCart = function(index) { state.cart.splice(index, 1); try { localStorage.setItem('nexus_cart', JSON.stringify(state.cart)); } catch(e){} openCartModal(); renderMainView(); }; window.nexusApplyPromoCode = function() { const code = document.getElementById('cartPromoInput')?.value?.trim().toUpperCase(); if (!code) return; if (code === 'NEXUS100') { state.appliedPromoCode = { code: 'NEXUS100', discount: 100 }; showToast('ใช้โค้ด NEXUS100 ส่วนลด ฿100 สำเร็จ!', 'ph-tag'); } else if (code === 'NEXUS500') { state.appliedPromoCode = { code: 'NEXUS500', discount: 500 }; showToast('ใช้โค้ด NEXUS500 ส่วนลด ฿500 สำเร็จ!', 'ph-tag'); } else { showToast('ไม่พบโค้ดส่วนลดนี้ หรือโค้ดหมดอายุแล้ว', 'ph-x'); } openCartModal(); }; window.nexusToggleCoins = function(checked) { state.useCoins = checked; openCartModal(); }; // Shopee-style Checkout Options & Payment Method Screen window.nexusProceedCheckoutPage = function() { const subtotal = state.cart.reduce((sum, item) => sum + (item.price * item.qty), 0); const shippingFee = (subtotal >= 3000 || subtotal === 0) ? 0 : 50; let discountAmount = state.mysteryCoupon ? (subtotal * state.mysteryCoupon) / 100 : (state.appliedPromoCode ? state.appliedPromoCode.discount : 0); let coinsDiscount = state.useCoins ? Math.min(state.coins, subtotal) : 0; const finalTotal = Math.max(0, subtotal - discountAmount - coinsDiscount + shippingFee); openModal(` <div style="max-width:650px; margin:0 auto; text-align:left;"> <h2 style="font-size:1.4rem; font-weight:800; margin-bottom:1rem; display:flex; align-items:center; gap:8px;"> <i class="ph ph-shield-check" style="color:var(--emerald-accent);"></i> ยืนยันการสั่งซื้อและชำระเงิน (Checkout & Payment) </h2> <!-- Address Selection --> <div class="glass-panel" style="padding:1rem; margin-bottom:1rem;"> <h4 style="font-size:0.9rem; font-weight:700; color:var(--teal-accent); margin-bottom:0.5rem;"><i class="ph ph-map-pin"></i> ที่อยู่จัดส่งพัสดุ (Shipping Address)</h4> <div style="font-size:0.85rem; color:#FFF; line-height:1.5;"> <strong>คุณ สมศักดิ์ พรหมดี (081-234-5678)</strong><br> <span style="color:var(--text-muted);">99/45 อาคารไซเบอร์ทาวเวอร์ ชั้น 12 ถนนสุขุมวิท แขวงคลองเตย เขตคลองเตย กรุงเทพมหานคร 10110</span> </div> </div> <!-- Payment Method Options --> <div class="glass-panel" style="padding:1rem; margin-bottom:1.25rem;"> <h4 style="font-size:0.9rem; font-weight:700; color:var(--gold-accent); margin-bottom:0.75rem;"><i class="ph ph-wallet"></i> เลือกช่องทางการชำระเงิน (Payment Method)</h4> <div style="display:flex; flex-direction:column; gap:0.6rem; font-size:0.85rem;"> <label style="display:flex; align-items:center; gap:10px; background:rgba(255,255,255,0.03); padding:10px 14px; border-radius:10px; cursor:pointer; border:1px solid var(--border-glass);"> <input type="radio" name="payMethod" value="qr" checked style="width:16px; height:16px;"> <i class="ph ph-qr-code" style="font-size:1.2rem; color:var(--teal-accent);"></i> <div><strong>สแกน QR พร้อมเพย์ (PromptPay QR)</strong> — ฟรีค่าธรรมเนียม, ยืนยันยอดทันที</div> </label> <label style="display:flex; align-items:center; gap:10px; background:rgba(255,255,255,0.03); padding:10px 14px; border-radius:10px; cursor:pointer; border:1px solid var(--border-glass);"> <input type="radio" name="payMethod" value="card" style="width:16px; height:16px;"> <i class="ph ph-credit-card" style="font-size:1.2rem; color:var(--primary-purple);"></i> <div><strong>บัตรเครดิต / เดบิต (Credit & Debit Card)</strong> — Visa, Mastercard, JCB</div> </label> <label style="display:flex; align-items:center; gap:10px; background:rgba(255,255,255,0.03); padding:10px 14px; border-radius:10px; cursor:pointer; border:1px solid var(--border-glass);"> <input type="radio" name="payMethod" value="cod" style="width:16px; height:16px;"> <i class="ph ph-truck" style="font-size:1.2rem; color:var(--gold-accent);"></i> <div><strong>เก็บเงินปลายทาง (Cash on Delivery - COD)</strong> — ชำระเงินเมื่อได้รับสินค้า</div> </label> </div> </div> <!-- Total Pay Summary Bar --> <div style="display:flex; justify-content:space-between; align-items:center; background:rgba(0,0,0,0.4); padding:1rem; border-radius:12px; margin-bottom:1.25rem;"> <div> <span style="font-size:0.8rem; color:var(--text-muted);">ยอดชำระรวมทั้งหมด</span> <div class="current-price" style="font-size:1.6rem;">฿${numberFormat(finalTotal)}</div> </div> <button class="btn-primary" style="padding:0.8rem 1.8rem; font-size:1.05rem;" onclick="window.nexusConfirmFinalOrder()"> <i class="ph ph-check-circle"></i> ยืนยันชำระเงิน </button> </div> </div> `); }; window.nexusConfirmFinalOrder = function() { const orderSn = 'NEX-' + Math.floor(100000 + Math.random() * 900000); // Deduct coins if used if (state.useCoins) { state.coins = 0; state.useCoins = false; } openModal(` <div style="max-width:550px; margin:0 auto; text-align:center;"> <i class="ph ph-check-circle" style="font-size:4rem; color:var(--emerald-accent); margin-bottom:0.5rem; animation:pulse 1s infinite;"></i> <h2 style="font-size:1.7rem; font-weight:800; color:#FFF;">ชำระเงินสำเร็จแล้ว!</h2> <p style="color:var(--text-muted); font-size:0.88rem; margin-bottom:1.5rem;">คำสั่งซื้อหมายเลข <strong>#${orderSn}</strong> ได้รับการยืนยันแล้ว</p> <!-- Delivery Timeline Visualizer (Thai) --> <div class="timeline-stepper" style="margin-bottom:1.5rem;"> <div class="timeline-step completed"> <div class="step-circle"><i class="ph ph-check"></i></div> <span>ยืนยันออเดอร์</span> </div> <div class="timeline-step active"> <div class="step-circle"><i class="ph ph-package"></i></div> <span>เตรียมจัดส่ง</span> </div> <div class="timeline-step"> <div class="step-circle"><i class="ph ph-truck"></i></div> <span>ขนส่งรับพัสดุ</span> </div> <div class="timeline-step"> <div class="step-circle"><i class="ph ph-house"></i></div> <span>จัดส่งสำเร็จ</span> </div> </div> <button class="btn-primary" style="width:100%; padding:0.85rem;" onclick="state.cart=[]; try{localStorage.removeItem('nexus_cart');}catch(e){} closeModal(); renderMainView();"> <i class="ph ph-house"></i> กลับสู่หน้าหลัก </button> </div> `); showToast(`สั่งซื้อสำเร็จ! เลขที่ออเดอร์ #${orderSn}`, 'ph-check-circle'); }; // 11. Seller Dashboard View function renderSellerDashboard() { appContainer.innerHTML = ` <section style="max-width:1300px; margin:2rem auto; padding:0 2rem;"> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:2rem;"> <div> <h1 style="font-size:2rem; font-weight:800;">Seller Command Center</h1> <p style="color:var(--text-muted); font-size:0.9rem;">CyberPulse Official Store Dashboard & Inventory Intelligence</p> </div> <button class="btn-primary" onclick="window.nexusOpenSellerAITool()"><i class="ph ph-sparkle"></i> AI Copywriter & SEO Generator</button> </div> <!-- Metrics Grid --> <div style="display:grid; grid-template-columns: repeat(4, 1fr); gap:1.25rem; margin-bottom:2rem;"> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">TOTAL REVENUE</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);">+18.4% this month</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">TOTAL 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);">99.2% Fulfillment Rate</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">STORE TRUST SCORE</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--gold-accent);">98/100</h2> <span style="font-size:0.75rem; color:var(--gold-accent);">Verified Seller Badge</span> </div> <div class="glass-panel" style="padding:1.25rem;"> <span style="font-size:0.8rem; color:var(--text-muted);">INVENTORY ALERT</span> <h2 style="font-size:1.8rem; font-weight:800; font-family:'Outfit'; color:var(--rose-accent);">2 SKUs Low</h2> <span style="font-size:0.75rem; color:var(--rose-accent);">Restock Suggested</span> </div> </div> <!-- Inventory Intelligence Alert Box --> <div style="background:rgba(244,63,94,0.1); border:1px solid var(--rose-accent); padding:1rem; border-radius:14px; margin-bottom:2rem; font-size:0.85rem;"> <h4 style="color:var(--rose-accent); font-weight:700;"><i class="ph ph-warning"></i> Inventory Intelligence Warning</h4> <p style="margin-top:0.3rem; color:var(--text-main);">⚠️ <strong>Futuristic Cyberpunk Oversized Hoodie:</strong> Stock is depleted down to 12 items. Demand spike (+32%) detected. Re-order 50 units within 4 days to avoid stockout.</p> </div> </section> `; } window.nexusOpenSellerAITool = function() { openModal(` <div style="max-width:600px; margin:0 auto;"> <h2 style="font-size:1.4rem; font-weight:800; margin-bottom:1rem;"><i class="ph ph-sparkle" style="color:var(--teal-accent);"></i> AI Seller Assistant</h2> <input type="text" id="sellerProductTitle" placeholder="Product Title e.g. NEXUS Wireless Earbuds" 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.nexusGenerateDescription()">Generate Description & SEO Tags</button> <div id="sellerAIOutput" style="margin-top:1.5rem;"></div> </div> `); }; window.nexusGenerateDescription = async function() { const title = document.getElementById('sellerProductTitle')?.value; const output = document.getElementById('sellerAIOutput'); if (!title || !output) return; output.innerHTML = `<p style="font-size:0.85rem; color:var(--teal-accent);">Generating AI marketing description & SEO keywords...</p>`; try { 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') { output.innerHTML = ` <div style="background:rgba(255,255,255,0.04); border:1px solid var(--border-glass); border-radius:12px; padding:1rem; text-align:left; font-size:0.85rem; white-space:pre-wrap;"> ${data.generated_description} <div style="margin-top:1rem; color:var(--teal-accent);"> <strong>SEO Tags:</strong> ${data.seo_keywords.join(' ')} </div> </div> `; } } catch (e) { console.error(e); } }; // 12. Admin Command Center View function renderAdminDashboard() { appContainer.innerHTML = ` <section style="max-width:1300px; margin:2rem auto; padding:0 2rem;"> <h1 style="font-size:2rem; font-weight:800; margin-bottom:0.5rem;">Platform Admin Command Center</h1> <p style="color:var(--text-muted); font-size:0.9rem; margin-bottom:2rem;">Global Marketplace Volume, Commission & Moderation</p> <div style="display:grid; grid-template-columns: repeat(3, 1fr); gap:1.5rem;"> <div class="glass-panel" style="padding:1.5rem;"> <span style="font-size:0.85rem; color:var(--text-muted);">GROSS MARKETPLACE VOLUME (GMV)</span> <h2 style="font-size:2.2rem; font-weight:900; font-family:'Outfit'; color:var(--teal-accent);">฿4,820,500</h2> </div> <div class="glass-panel" style="padding:1.5rem;"> <span style="font-size:0.85rem; color:var(--text-muted);">PLATFORM COMMISSION (5%)</span> <h2 style="font-size:2.2rem; font-weight:900; font-family:'Outfit'; color:var(--emerald-accent);">฿241,025</h2> </div> <div class="glass-panel" style="padding:1.5rem;"> <span style="font-size:0.85rem; color:var(--text-muted);">ACTIVE USERS & SELLERS</span> <h2 style="font-size:2.2rem; font-weight:900; font-family:'Outfit'; color:var(--gold-accent);">12,480</h2> </div> </div> </section> `; } // Helpers & Global Handlers window.nexusFilterCategory = function(cat) { state.activeCategory = cat; fetchProducts(cat).then(renderMainView); }; window.nexusNav = function(view) { state.currentView = view; renderMainView(); }; window.nexusToggleAlert = function(productId) { showToast('Price Drop Alert set! We will notify you when price drops.', 'ph-bell'); }; window.nexusOpenLoginSelectorModal = function() { window.location.href = 'login.php'; }; window.nexusQuickLoginRole = async function(role) { let username = 'alexvance'; let password = 'password123'; if (role === 'seller') { username = 'cyberpulse'; password = 'password123'; } else if (role === 'admin') { username = 'admin_nexus'; password = 'adminpass'; } try { const res = await fetch('api/login.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'login', username, password, portal: role }) }); const data = await res.json(); if (data.status === 'success') { closeModal(); if (role === 'seller') { window.location.href = 'seller.php'; } else if (role === 'admin') { window.location.href = 'admin.php'; } else { state.role = 'customer'; state.currentView = 'home'; location.reload(); } } } catch (e) { console.error(e); } }; function openModal(contentHtml) { if (modalBody && modalOverlay) { modalBody.innerHTML = contentHtml; modalOverlay.classList.add('open'); } } window.closeModal = function() { if (modalOverlay) modalOverlay.classList.remove('open'); }; document.getElementById('modalCloseBtn')?.addEventListener('click', closeModal); // Close modal when clicking outside on overlay backdrop modalOverlay?.addEventListener('click', (e) => { if (e.target === modalOverlay) { closeModal(); } }); // Close modal on Escape key press document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeModal(); } }); window.nexusLogout = async function() { if (confirm('คุณต้องการออกจากระบบหรือไม่?')) { await fetch('api/login.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'logout' }) }); window.location.href = 'login.php'; } }; function numberFormat(num) { return new Intl.NumberFormat().format(num || 0); } });
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.28 |
proxy
|
phpinfo
|
Settings