File manager - Edit - /home/webapp69.cm.in.th/u69319090005/shop005/js/app.js
Back
/* ========================================================================== BABYMONSTER FASHION SHOP - CORE APPLICATION LOGIC ========================================================================== */ class BabyMonsterShopApp { constructor() { // State Management this.products = PRODUCTS_DATA; this.filteredProducts = [...PRODUCTS_DATA]; this.cart = JSON.parse(localStorage.getItem('bm_cart')) || []; this.wishlist = JSON.parse(localStorage.getItem('bm_wishlist')) || []; this.currentUser = JSON.parse(localStorage.getItem('bm_user')) || { isLoggedIn: true, name: 'MONSTER FAN', email: 'monster.fan@babymonster.com', phone: '0812345678' }; this.orders = JSON.parse(localStorage.getItem('bm_orders')) || [ { id: 'ORD-2026-8941', date: '24 ก.ค. 2026', status: 'shipping', statusText: 'กำลังจัดส่งพัสดุ', trackingNo: 'TH2607998124BM', courier: 'KERRY EXPRESS', items: [ { name: 'BABYMONSTER World Tour "SHEESH" Limited Hoodie', qty: 1, price: 1890, color: 'Black', size: 'L' } ], total: 1890 } ]; this.appliedCoupon = null; this.activeCategory = 'all'; this.activeTab = 'best-sellers'; this.activeFilters = { category: 'all', minPrice: null, maxPrice: null, colors: [], sizes: [] }; this.selectedProduct = null; this.selectedColor = null; this.selectedSize = null; this.selectedQty = 1; // Auto-init on DOM Ready document.addEventListener('DOMContentLoaded', () => this.init()); } init() { this.bindEvents(); this.initSplashScreen(); this.initHeroCarousel(); this.initFlashSaleTimer(); this.renderAll(); this.updateHeaderBadges(); } // Splash Screen initSplashScreen() { const enterBtn = document.getElementById('btn-enter-shop'); const splash = document.getElementById('splash-screen'); if (enterBtn && splash) { enterBtn.addEventListener('click', () => { splash.classList.add('fade-out'); setTimeout(() => splash.remove(), 600); }); // Auto hide splash after 3.5 seconds if user doesn't click setTimeout(() => { if (splash.parentNode) { splash.classList.add('fade-out'); setTimeout(() => splash.remove(), 600); } }, 3500); } } // Event Listeners Binding bindEvents() { // Search Input const searchInput = document.getElementById('search-input'); const clearSearchBtn = document.getElementById('btn-clear-search'); if (searchInput) { searchInput.addEventListener('input', (e) => this.handleSearch(e.target.value)); } if (clearSearchBtn) { clearSearchBtn.addEventListener('click', () => { searchInput.value = ''; clearSearchBtn.classList.add('hide'); this.handleSearch(''); }); } // Category Pills const categoryPills = document.getElementById('category-pills'); if (categoryPills) { categoryPills.addEventListener('click', (e) => { const pill = e.target.closest('.cat-pill'); if (pill) { document.querySelectorAll('.cat-pill').forEach(p => p.classList.remove('active')); pill.classList.add('active'); this.filterByCategory(pill.dataset.category); } }); } // Sort Selector const sortSelect = document.getElementById('sort-select'); if (sortSelect) { sortSelect.addEventListener('change', (e) => this.handleSort(e.target.value)); } // Tab Buttons document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', (e) => { document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); this.activeTab = btn.dataset.tab; this.renderCuratedProducts(); }); }); // Header Buttons document.getElementById('btn-cart-toggle')?.addEventListener('click', () => this.toggleCart(true)); document.getElementById('btn-wishlist-toggle')?.addEventListener('click', () => this.openUserProfile('wishlist')); document.getElementById('btn-user-profile')?.addEventListener('click', () => this.openUserProfile('orders')); document.getElementById('btn-tiktok-mode')?.addEventListener('click', () => this.openTikTokFeed()); document.getElementById('btn-floating-tiktok')?.addEventListener('click', () => this.openTikTokFeed()); document.getElementById('btn-close-tiktok')?.addEventListener('click', () => this.closeTikTokFeed()); // Notifications & Admin Buttons document.getElementById('btn-notifications-toggle')?.addEventListener('click', () => this.toggleNotificationsDrawer(true)); document.getElementById('btn-admin-portal')?.addEventListener('click', () => this.openAdminModal()); // Coupon apply button document.getElementById('btn-apply-coupon')?.addEventListener('click', () => this.applyCouponCode()); // Checkout Button document.getElementById('btn-proceed-checkout')?.addEventListener('click', () => this.openCheckout()); // Filter Drawer Triggers document.getElementById('btn-open-filters')?.addEventListener('click', () => this.toggleFilterDrawer(true)); document.getElementById('btn-reset-filters')?.addEventListener('click', () => this.resetAllFilters()); // Chat Widget Toggle document.getElementById('btn-toggle-chat')?.addEventListener('click', () => this.toggleChat()); } // Hero Banner Auto Carousel initHeroCarousel() { let currentSlide = 0; const slides = document.querySelectorAll('.hero-slide'); const indicators = document.querySelectorAll('.carousel-indicators .indicator'); if (slides.length <= 1) return; const changeSlide = (index) => { slides.forEach(s => s.classList.remove('active')); indicators.forEach(i => i.classList.remove('active')); slides[index].classList.add('active'); if (indicators[index]) indicators[index].classList.add('active'); currentSlide = index; }; setInterval(() => { const nextSlide = (currentSlide + 1) % slides.length; changeSlide(nextSlide); }, 5000); indicators.forEach((ind, i) => { ind.addEventListener('click', () => changeSlide(i)); }); } // Flash Sale Countdown Timer Simulation initFlashSaleTimer() { let hours = 4, mins = 32, secs = 15; const hElem = document.getElementById('timer-hours'); const mElem = document.getElementById('timer-mins'); const sElem = document.getElementById('timer-secs'); setInterval(() => { if (secs > 0) { secs--; } else { secs = 59; if (mins > 0) { mins--; } else { mins = 59; if (hours > 0) hours--; } } if (hElem) hElem.textContent = String(hours).padStart(2, '0'); if (mElem) mElem.textContent = String(mins).padStart(2, '0'); if (sElem) sElem.textContent = String(secs).padStart(2, '0'); }, 1000); } // Render Master Method renderAll() { this.renderFlashSale(); this.renderCuratedProducts(); this.renderMainProductsCatalog(); this.renderCart(); } // Flash Sale Products Render renderFlashSale() { const flashContainer = document.getElementById('flash-sale-grid'); if (!flashContainer) return; const flashProducts = this.products.filter(p => p.isFlashSale); flashContainer.innerHTML = flashProducts.map(p => this.createProductCardHTML(p, true)).join(''); } // Curated Tabs Render (Best Sellers, New Arrivals, Recommended) renderCuratedProducts() { const container = document.getElementById('curated-products-grid'); if (!container) return; let filtered = [...this.products]; if (this.activeTab === 'best-sellers') { filtered = filtered.filter(p => p.isBestSeller); } else if (this.activeTab === 'new-arrivals') { filtered = filtered.filter(p => p.isNewArrival); } else { filtered = filtered.filter(p => p.rating >= 4.8); } container.innerHTML = filtered.map(p => this.createProductCardHTML(p)).join(''); } // Main Catalog Render with Filter Checks renderMainProductsCatalog() { const container = document.getElementById('main-products-grid'); const countElem = document.getElementById('products-count'); const emptyElem = document.getElementById('no-products-found'); if (!container) return; let list = [...this.filteredProducts]; // Active Category if (this.activeFilters.category !== 'all') { list = list.filter(p => p.category === this.activeFilters.category); } // Min & Max Price if (this.activeFilters.minPrice !== null) { list = list.filter(p => p.price >= this.activeFilters.minPrice); } if (this.activeFilters.maxPrice !== null) { list = list.filter(p => p.price <= this.activeFilters.maxPrice); } // Color Filter if (this.activeFilters.colors.length > 0) { list = list.filter(p => p.colors.some(c => this.activeFilters.colors.includes(c))); } // Size Filter if (this.activeFilters.sizes.length > 0) { list = list.filter(p => p.sizes.some(s => this.activeFilters.sizes.includes(s))); } if (countElem) countElem.textContent = list.length; if (list.length === 0) { container.innerHTML = ''; emptyElem?.classList.remove('hide'); } else { emptyElem?.classList.add('hide'); container.innerHTML = list.map(p => this.createProductCardHTML(p)).join(''); } this.renderActiveFilterTags(); } // Product Card Template Generator (Matching Reference Image Exact Design) createProductCardHTML(product) { const isWishlisted = this.wishlist.includes(product.id); const discountLabel = product.discountPercent || (product.originalPrice ? `-${Math.round(((product.originalPrice - product.price) / product.originalPrice) * 100)}%` : ''); return ` <div class="product-card" data-id="${product.id}"> <div class="card-img-wrapper" onclick="app.openProductDetail('${product.id}')"> <img src="${product.image}" alt="${product.name}" loading="lazy"> ${discountLabel ? `<span class="card-badge-pill">${discountLabel}</span>` : ''} <button class="btn-quick-wishlist ${isWishlisted ? 'active' : ''}" onclick="event.stopPropagation(); app.toggleWishlist('${product.id}')" title="เพิ่มลงในรายการโปรด"> <i class="fa-${isWishlisted ? 'solid' : 'regular'} fa-heart"></i> </button> </div> <div class="card-info" onclick="app.openProductDetail('${product.id}')"> <div class="card-category">${product.categoryName}</div> <h3 class="card-title">${product.name}</h3> <div class="card-rating"> <i class="fa-solid fa-star"></i> <span>${product.rating}</span> <span class="rating-count">(${product.reviewsCount} รีวิว)</span> </div> <div class="card-price-row"> <div class="price-wrap"> <span class="price-current">฿${product.price.toLocaleString()}</span> ${product.originalPrice ? `<span class="price-original">฿${product.originalPrice.toLocaleString()}</span>` : ''} </div> <button class="btn-add-cart-square" onclick="event.stopPropagation(); app.quickAddToCart('${product.id}')" title="สั่งซื้อสินค้า"> <i class="fa-solid fa-bag-shopping"></i> </button> </div> </div> </div> `; } // Search Handler handleSearch(query) { const clearBtn = document.getElementById('btn-clear-search'); const suggestions = document.getElementById('search-suggestions'); const q = query.trim().toLowerCase(); if (q.length > 0) { clearBtn?.classList.remove('hide'); this.filteredProducts = this.products.filter(p => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q) || p.categoryName.toLowerCase().includes(q) ); // Render live suggestions drop if (suggestions) { const matches = this.filteredProducts.slice(0, 5); if (matches.length > 0) { suggestions.innerHTML = matches.map(p => ` <div class="suggestion-item" onclick="app.openProductDetail('${p.id}'); document.getElementById('search-suggestions').classList.add('hide');"> <img src="${p.image}" class="suggestion-img"> <div> <strong>${p.name}</strong> <div class="text-danger">฿${p.price.toLocaleString()}</div> </div> </div> `).join(''); suggestions.classList.remove('hide'); } else { suggestions.classList.add('hide'); } } } else { clearBtn?.classList.add('hide'); suggestions?.classList.add('hide'); this.filteredProducts = [...this.products]; } this.renderMainProductsCatalog(); } // Sorting Handler handleSort(sortBy) { if (sortBy === 'price-asc') { this.filteredProducts.sort((a, b) => a.price - b.price); } else if (sortBy === 'price-desc') { this.filteredProducts.sort((a, b) => b.price - a.price); } else if (sortBy === 'newest') { this.filteredProducts.sort((a, b) => (b.isNewArrival ? 1 : 0) - (a.isNewArrival ? 1 : 0)); } else if (sortBy === 'rating') { this.filteredProducts.sort((a, b) => b.rating - a.rating); } else { this.filteredProducts.sort((a, b) => b.reviewsCount - a.reviewsCount); } this.renderMainProductsCatalog(); } // Filter by Category Pill filterByCategory(category) { this.activeFilters.category = category; this.renderMainProductsCatalog(); this.scrollToProducts(); } filterByTag(tag) { if (tag === 'flash_sale') { this.filteredProducts = this.products.filter(p => p.isFlashSale); } this.renderMainProductsCatalog(); this.scrollToProducts(); } scrollToProducts() { document.getElementById('products-catalog-section')?.scrollIntoView({ behavior: 'smooth' }); } // Filter Drawer Management toggleFilterDrawer(show = true) { const drawer = document.getElementById('filter-drawer'); if (show) { // Render category options in drawer const catContainer = document.getElementById('filter-category-options'); if (catContainer) { const cats = [ { id: 'all', name: 'ทั้งหมด' }, { id: 'hoodie', name: 'ฮู้ดดี้' }, { id: 'tshirt', name: 'เสื้อยืด' }, { id: 'jacket', name: 'แจ็กเก็ต' }, { id: 'pants', name: 'กางเกง' }, { id: 'cap', name: 'หมวก' }, { id: 'bag', name: 'กระเป๋า' }, { id: 'accessory', name: 'เครื่องประดับ' } ]; catContainer.innerHTML = cats.map(c => ` <label class="cat-pill ${this.activeFilters.category === c.id ? 'active' : ''}"> <input type="radio" name="filter_cat" value="${c.id}" ${this.activeFilters.category === c.id ? 'checked' : ''} style="display:none"> ${c.name} </label> `).join(''); catContainer.querySelectorAll('label').forEach(lbl => { lbl.addEventListener('click', () => { catContainer.querySelectorAll('label').forEach(l => l.classList.remove('active')); lbl.classList.add('active'); }); }); } drawer?.classList.remove('hide'); } else { drawer?.classList.add('hide'); } } applyFilters() { const catRadio = document.querySelector('input[name="filter_cat"]:checked'); const minP = document.getElementById('filter-min-price')?.value; const maxP = document.getElementById('filter-max-price')?.value; const checkedColors = Array.from(document.querySelectorAll('#filter-color-options input:checked')).map(i => i.value); const checkedSizes = Array.from(document.querySelectorAll('#filter-size-options input:checked')).map(i => i.value); this.activeFilters = { category: catRadio ? catRadio.value : 'all', minPrice: minP ? parseFloat(minP) : null, maxPrice: maxP ? parseFloat(maxP) : null, colors: checkedColors, sizes: checkedSizes }; this.renderMainProductsCatalog(); this.toggleFilterDrawer(false); this.showToast('อัปเดตตัวกรองสินค้าแล้ว'); } resetAllFilters() { this.activeFilters = { category: 'all', minPrice: null, maxPrice: null, colors: [], sizes: [] }; this.filteredProducts = [...this.products]; document.getElementById('search-input').value = ''; this.renderMainProductsCatalog(); this.toggleFilterDrawer(false); this.showToast('ล้างตัวกรองทั้งหมดเรียบร้อย'); } renderActiveFilterTags() { const bar = document.getElementById('active-filters-bar'); const tagsContainer = document.getElementById('filter-tags-container'); if (!bar || !tagsContainer) return; let tags = []; if (this.activeFilters.category !== 'all') tags.push(`หมวดหมู่: ${this.activeFilters.category}`); if (this.activeFilters.minPrice) tags.push(`ราคาขั้นต่ำ ฿${this.activeFilters.minPrice}`); if (this.activeFilters.maxPrice) tags.push(`ราคาสูงสุด ฿${this.activeFilters.maxPrice}`); if (this.activeFilters.colors.length > 0) tags.push(`สี: ${this.activeFilters.colors.join(', ')}`); if (this.activeFilters.sizes.length > 0) tags.push(`ไซส์: ${this.activeFilters.sizes.join(', ')}`); if (tags.length > 0) { tagsContainer.innerHTML = tags.map(t => `<span class="filter-chip">${t}</span>`).join(''); bar.classList.remove('hide'); } else { bar.classList.add('hide'); } } // Product Detail Modal View openProductDetail(productId) { const product = this.products.find(p => p.id === productId); if (!product) return; this.selectedProduct = product; this.selectedColor = product.colors[0]; this.selectedSize = product.sizes[0]; this.selectedQty = 1; const modal = document.getElementById('product-detail-modal'); const content = document.getElementById('product-detail-content'); content.innerHTML = ` <div class="detail-gallery"> <div class="main-preview-box"> <img id="detail-main-img" src="${product.image}" alt="${product.name}"> </div> <div class="gallery-thumbs"> ${product.gallery.map((img, i) => ` <div class="thumb-item ${i === 0 ? 'active' : ''}" onclick="app.changeDetailThumb('${img}', this)"> <img src="${img}"> </div> `).join('')} </div> </div> <div class="detail-info"> <span class="detail-category"><i class="fa-solid fa-fire"></i> ${product.categoryName} | ลิขสิทธิ์แท้</span> <h2 class="detail-title">${product.name}</h2> <div class="detail-rating"> <i class="fa-solid fa-star"></i> <strong>${product.rating} / 5.0</strong> <span>(${product.reviewsCount} รีวิวจากลูกค้า)</span> <span class="badge badge-danger">สินค้าพร้อมส่ง</span> </div> <div class="detail-price-box"> <span class="detail-current-price">฿${product.price.toLocaleString()}</span> ${product.originalPrice ? `<span class="detail-original-price">฿${product.originalPrice.toLocaleString()}</span>` : ''} </div> <p style="margin-bottom: 20px; color: #cbd5e1; font-size: 0.95rem;">${product.description}</p> <!-- Color Selection --> <div class="option-group"> <label class="option-label">เลือกสี (Color):</label> <div class="color-options"> ${product.colors.map(c => ` <button class="btn-option ${c === this.selectedColor ? 'selected' : ''}" onclick="app.selectDetailColor('${c}', this)">${c}</button> `).join('')} </div> </div> <!-- Size Selection --> <div class="option-group"> <label class="option-label">เลือกขนาด (Size):</label> <div class="size-options"> ${product.sizes.map(s => ` <button class="btn-option ${s === this.selectedSize ? 'selected' : ''}" onclick="app.selectDetailSize('${s}', this)">${s}</button> `).join('')} </div> </div> <!-- Quantity Control --> <div class="option-group"> <label class="option-label">จำนวน (Quantity):</label> <div class="quantity-control"> <button class="btn-qty" onclick="app.changeDetailQty(-1)"><i class="fa-solid fa-minus"></i></button> <input type="text" id="detail-qty-input" class="qty-input" value="1" readonly> <button class="btn-qty" onclick="app.changeDetailQty(1)"><i class="fa-solid fa-plus"></i></button> </div> <span style="font-size: 0.8rem; color: var(--text-muted); margin-left: 10px;">มีสินค้าคงเหลือ ${product.stock} ชิ้น</span> </div> <!-- Detail Action Buttons --> <div class="detail-actions"> <button class="btn btn-outline btn-lg" onclick="app.addToCartFromDetail()"><i class="fa-solid fa-cart-plus"></i> เพิ่มลงตะกร้า</button> <button class="btn btn-primary btn-lg" onclick="app.buyNowFromDetail()"><i class="fa-solid fa-bolt"></i> สั่งซื้อทันที</button> </div> <!-- Related Products Section --> <div class="reviews-section" style="margin-top:20px; border-top:1px solid var(--border-color); padding-top:16px;"> <h4><i class="fa-solid fa-layer-group text-danger"></i> สินค้าที่เกี่ยวข้องที่คุณอาจชอบ</h4> <div style="display:flex; gap:10px; overflow-x:auto; padding-top:10px;"> ${this.products.filter(p => p.category === product.category && p.id !== product.id).slice(0, 3).map(rp => ` <div style="min-width:140px; background:var(--bg-surface); padding:8px; border-radius:8px; cursor:pointer;" onclick="app.openProductDetail('${rp.id}')"> <img src="${rp.image}" style="width:100%; aspect-ratio:1; object-fit:cover; border-radius:6px;"> <div style="font-size:0.78rem; font-weight:600; margin-top:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${rp.name}</div> <div style="font-size:0.85rem; color:var(--primary-red); font-weight:800;">฿${rp.price.toLocaleString()}</div> </div> `).join('')} </div> </div> <!-- Add Review Form & Reviews List --> <div class="reviews-section"> <h4><i class="fa-solid fa-star"></i> เขียนรีวิวสินค้า</h4> <form onsubmit="app.submitProductReview(event)" style="margin-bottom:15px; display:flex; flex-direction:column; gap:8px;"> <div style="display:flex; gap:10px; align-items:center;"> <label style="font-size:0.85rem;">ให้คะแนน:</label> <select id="review-rating-input" class="custom-select" style="padding:4px 8px; font-size:0.85rem;"> <option value="5">⭐⭐⭐⭐⭐ (5/5)</option> <option value="4">⭐⭐⭐⭐ (4/5)</option> <option value="3">⭐⭐⭐ (3/5)</option> </select> </div> <textarea id="review-comment-input" rows="2" required placeholder="แบ่งปันความรู้สึกประทับใจเกี่ยวกับสินค้า..." style="padding:8px; background:var(--bg-input); border:1px solid var(--border-color); border-radius:6px; font-size:0.85rem;"></textarea> <button type="submit" class="btn btn-primary btn-sm" style="align-self:flex-end;"><i class="fa-solid fa-paper-plane"></i> ส่งรีวิว</button> </form> <h4><i class="fa-solid fa-comments"></i> รีวิวจากลูกค้าล่าสุด</h4> <div id="product-reviews-container"> ${SAMPLE_REVIEWS.map(r => ` <div class="review-item"> <div class="review-user"> <span>${r.name}</span> <span style="color:#ffb703;">★ ${r.rating}.0 (${r.date})</span> </div> <p style="font-size: 0.85rem; color: #cbd5e1; margin-top:4px;">${r.comment}</p> </div> `).join('')} </div> </div> </div> `; modal.classList.remove('hide'); } submitProductReview(event) { event.preventDefault(); const rating = document.getElementById('review-rating-input').value; const comment = document.getElementById('review-comment-input').value; const container = document.getElementById('product-reviews-container'); if (!comment || !container) return; const newReview = { name: this.currentUser.name || 'MONSTER FAN', rating: parseInt(rating), date: 'วันนี้', comment: comment }; SAMPLE_REVIEWS.unshift(newReview); if (this.selectedProduct) { this.selectedProduct.reviewsCount += 1; } container.innerHTML = SAMPLE_REVIEWS.map(r => ` <div class="review-item"> <div class="review-user"> <span>${r.name}</span> <span style="color:#ffb703;">★ ${r.rating}.0 (${r.date})</span> </div> <p style="font-size: 0.85rem; color: #cbd5e1; margin-top:4px;">${r.comment}</p> </div> `).join(''); event.target.reset(); this.showToast('🌟 ขอบคุณสำหรับรีวิวของคุณ!'); } closeProductDetail() { document.getElementById('product-detail-modal')?.classList.add('hide'); } changeDetailThumb(imgUrl, elem) { document.getElementById('detail-main-img').src = imgUrl; document.querySelectorAll('.thumb-item').forEach(t => t.classList.remove('active')); elem.classList.add('active'); } selectDetailColor(color, elem) { this.selectedColor = color; elem.parentNode.querySelectorAll('.btn-option').forEach(b => b.classList.remove('selected')); elem.classList.add('selected'); } selectDetailSize(size, elem) { this.selectedSize = size; elem.parentNode.querySelectorAll('.btn-option').forEach(b => b.classList.remove('selected')); elem.classList.add('selected'); } changeDetailQty(delta) { const input = document.getElementById('detail-qty-input'); if (!input || !this.selectedProduct) return; let newQty = this.selectedQty + delta; if (newQty >= 1 && newQty <= this.selectedProduct.stock) { this.selectedQty = newQty; input.value = newQty; } } // TikTok Style Feed Viewer openTikTokFeed() { const modal = document.getElementById('tiktok-modal'); const container = document.getElementById('tiktok-container'); if (!modal || !container) return; container.innerHTML = TIKTOK_FEED_DATA.map(item => { const product = this.products.find(p => p.id === item.productId); return ` <div class="tiktok-card"> <video class="tiktok-video" loop autoplay muted playsinline src="${item.videoUrl}"></video> <div class="tiktok-overlay"> <div class="tiktok-header-info"> <img src="${item.avatar}" class="tiktok-avatar"> <div> <strong>${item.author}</strong> <div style="font-size:0.75rem; color:#aaa;">${item.username}</div> </div> </div> <div class="tiktok-side-actions"> <button class="tiktok-action-btn" onclick="app.showToast('❤️ ชอบวิดีโอนี้แล้ว!')"> <i class="fa-solid fa-heart text-danger"></i> <span>${item.likes}</span> </button> <button class="tiktok-action-btn"> <i class="fa-solid fa-comment-dots"></i> <span>${item.comments}</span> </button> <button class="tiktok-action-btn"> <i class="fa-solid fa-share"></i> <span>${item.shares}</span> </button> </div> <div> <p style="font-size:0.9rem; margin-bottom:10px;">${item.caption}</p> <div style="font-size:0.8rem; color:#00f2fe; margin-bottom:12px;"><i class="fa-solid fa-music"></i> ${item.song}</div> ${product ? ` <div class="tiktok-product-card"> <img src="${product.image}" class="tiktok-prod-img"> <div class="tiktok-prod-info"> <h4>${product.name}</h4> <div class="tiktok-prod-price">฿${product.price.toLocaleString()}</div> </div> <button class="btn btn-primary btn-sm" onclick="app.quickAddToCart('${product.id}')"> <i class="fa-solid fa-bag-shopping"></i> สั่งซื้อ </button> </div> ` : ''} </div> </div> </div> `; }).join(''); modal.classList.remove('hide'); } closeTikTokFeed() { document.getElementById('tiktok-modal')?.classList.add('hide'); } // Cart Management quickAddToCart(productId) { const product = this.products.find(p => p.id === productId); if (!product) return; this.addCartItem(product, product.colors[0], product.sizes[0], 1); } addToCartFromDetail() { if (!this.selectedProduct) return; this.addCartItem(this.selectedProduct, this.selectedColor, this.selectedSize, this.selectedQty); this.closeProductDetail(); } buyNowFromDetail() { if (!this.selectedProduct) return; this.addCartItem(this.selectedProduct, this.selectedColor, this.selectedSize, this.selectedQty); this.closeProductDetail(); this.openCheckout(); } addCartItem(product, color, size, qty) { const existingIndex = this.cart.findIndex(i => i.id === product.id && i.color === color && i.size === size); if (existingIndex > -1) { this.cart[existingIndex].qty += qty; } else { this.cart.push({ id: product.id, name: product.name, price: product.price, image: product.image, color: color, size: size, qty: qty }); } this.saveCart(); this.renderCart(); this.updateHeaderBadges(); this.showToast(`🛍️ เพิ่ม "${product.name}" เข้าตะกร้าแล้ว`); this.animateCartBadge(); } updateCartQty(index, delta) { if (this.cart[index]) { this.cart[index].qty += delta; if (this.cart[index].qty <= 0) { this.cart.splice(index, 1); } this.saveCart(); this.renderCart(); this.updateHeaderBadges(); } } removeCartItem(index) { this.cart.splice(index, 1); this.saveCart(); this.renderCart(); this.updateHeaderBadges(); this.showToast('ลบรายการสินค้าแล้ว'); } saveCart() { localStorage.setItem('bm_cart', JSON.stringify(this.cart)); } renderCart() { const bodyContainer = document.getElementById('cart-body-items'); const countElem = document.getElementById('cart-items-count'); const subtotalElem = document.getElementById('cart-subtotal'); const totalElem = document.getElementById('cart-total'); const shippingElem = document.getElementById('cart-shipping'); const discountElem = document.getElementById('cart-discount'); const discountRow = document.getElementById('row-discount'); if (!bodyContainer) return; const totalItemsCount = this.cart.reduce((sum, item) => sum + item.qty, 0); if (countElem) countElem.textContent = totalItemsCount; if (this.cart.length === 0) { bodyContainer.innerHTML = ` <div class="empty-state"> <i class="fa-solid fa-bag-shopping empty-icon"></i> <h4>ตะกร้าของคุณยังว่างอยู่</h4> <p style="font-size:0.85rem; color:#888;">เลือกชมสินค้าและเติมความสดใสให้ลุคของคุณ</p> </div> `; if (subtotalElem) subtotalElem.textContent = '฿0'; if (totalElem) totalElem.textContent = '฿0'; if (shippingElem) shippingElem.textContent = '฿0'; if (discountRow) discountRow.style.display = 'none'; return; } bodyContainer.innerHTML = this.cart.map((item, index) => ` <div class="cart-item"> <img src="${item.image}" class="cart-item-img"> <div class="cart-item-info"> <h4 class="cart-item-title">${item.name}</h4> <div class="cart-item-meta">ตัวเลือก: ${item.color} | ไซส์ ${item.size}</div> <div class="cart-item-price">฿${item.price.toLocaleString()}</div> <div class="quantity-control" style="margin-top:6px;"> <button class="btn-qty" onclick="app.updateCartQty(${index}, -1)"><i class="fa-solid fa-minus"></i></button> <input type="text" class="qty-input" value="${item.qty}" readonly> <button class="btn-qty" onclick="app.updateCartQty(${index}, 1)"><i class="fa-solid fa-plus"></i></button> </div> </div> <button class="btn-remove-cart" onclick="app.removeCartItem(${index})"><i class="fa-solid fa-trash-can"></i></button> </div> `).join(''); const subtotal = this.cart.reduce((sum, item) => sum + (item.price * item.qty), 0); let shipping = subtotal >= 1500 ? 0 : 60; let discount = 0; if (this.appliedCoupon) { if (this.appliedCoupon.discountType === 'percent') { discount = (subtotal * this.appliedCoupon.value) / 100; } else if (this.appliedCoupon.discountType === 'fixed') { discount = this.appliedCoupon.value; } else if (this.appliedCoupon.discountType === 'shipping') { shipping = 0; } } const grandTotal = Math.max(0, subtotal - discount + shipping); if (subtotalElem) subtotalElem.textContent = `฿${subtotal.toLocaleString()}`; if (shippingElem) shippingElem.textContent = shipping === 0 ? 'ส่งฟรี (FREE)' : `฿${shipping}`; if (totalElem) totalElem.textContent = `฿${grandTotal.toLocaleString()}`; if (discount > 0 && discountRow && discountElem) { discountRow.style.display = 'flex'; discountElem.textContent = `-฿${discount.toLocaleString()}`; } else if (discountRow) { discountRow.style.display = 'none'; } } applyCouponCode() { const input = document.getElementById('coupon-input'); const msgElem = document.getElementById('coupon-message'); if (!input || !msgElem) return; const code = input.value.trim().toUpperCase(); if (COUPONS_DATA[code]) { const coupon = COUPONS_DATA[code]; const subtotal = this.cart.reduce((sum, item) => sum + (item.price * item.qty), 0); if (subtotal < coupon.minSpend) { msgElem.style.color = '#ef4444'; msgElem.textContent = `⚠️ ต้องมียอดสั่งซื้อขั้นต่ำ ฿${coupon.minSpend} บาท`; return; } this.appliedCoupon = coupon; msgElem.style.color = '#10b981'; msgElem.textContent = `✅ ใช้โค้ด "${code}" (${coupon.label}) สำเร็จ!`; this.renderCart(); this.showToast(`🎉 ใช้ส่วนลด ${coupon.label} เรียบร้อยแล้ว`); } else { msgElem.style.color = '#ef4444'; msgElem.textContent = '❌ โค้ดส่วนลดไม่ถูกต้องหรือหมดอายุแล้ว'; } } toggleCart(show = true) { const drawer = document.getElementById('cart-drawer'); if (show) drawer?.classList.remove('hide'); else drawer?.classList.add('hide'); } // Wishlist Toggle toggleWishlist(productId) { const idx = this.wishlist.indexOf(productId); if (idx > -1) { this.wishlist.splice(idx, 1); this.showToast('ลบออกจากรายการโปรดแล้ว'); } else { this.wishlist.push(productId); this.showToast('❤️ บันทึกเข้าในรายการโปรดแล้ว'); } localStorage.setItem('bm_wishlist', JSON.stringify(this.wishlist)); this.updateHeaderBadges(); this.renderAll(); } updateHeaderBadges() { const cartBadge = document.getElementById('cart-badge'); const wishBadge = document.getElementById('wishlist-badge'); const userLabel = document.getElementById('header-user-name'); const totalQty = this.cart.reduce((sum, item) => sum + item.qty, 0); if (cartBadge) cartBadge.textContent = totalQty; if (wishBadge) wishBadge.textContent = this.wishlist.length; if (userLabel && this.currentUser.isLoggedIn) { userLabel.textContent = this.currentUser.name; } } animateCartBadge() { const cartBtn = document.getElementById('btn-cart-toggle'); if (cartBtn) { cartBtn.classList.add('pulse-animation'); setTimeout(() => cartBtn.classList.remove('pulse-animation'), 1000); } } // Checkout Flow openCheckout() { if (this.cart.length === 0) { this.showToast('⚠️ โปรดเลือกสินค้าลงตะกร้าก่อนดำเนินการชำระเงิน'); return; } this.toggleCart(false); // Pre-fill user data document.getElementById('ship-name').value = this.currentUser.name || ''; document.getElementById('ship-phone').value = this.currentUser.phone || ''; // Render Checkout Total & Payment const subtotal = this.cart.reduce((sum, item) => sum + (item.price * item.qty), 0); let shipping = subtotal >= 1500 ? 0 : 60; let discount = 0; if (this.appliedCoupon) { if (this.appliedCoupon.discountType === 'percent') { discount = (subtotal * this.appliedCoupon.value) / 100; } else if (this.appliedCoupon.discountType === 'fixed') { discount = this.appliedCoupon.value; } else if (this.appliedCoupon.discountType === 'shipping') { shipping = 0; } } const total = Math.max(0, subtotal - discount + shipping); document.getElementById('checkout-total-price').textContent = `฿${total.toLocaleString()}`; this.onPaymentChange('promptpay'); document.getElementById('checkout-modal')?.classList.remove('hide'); } closeCheckout() { document.getElementById('checkout-modal')?.classList.add('hide'); } onPaymentChange(method) { document.querySelectorAll('.payment-card').forEach(card => card.classList.remove('active')); const activeRadio = document.querySelector(`input[name="payment"][value="${method}"]`); if (activeRadio) activeRadio.closest('.payment-card').classList.add('active'); const detailsBox = document.getElementById('payment-details-box'); if (!detailsBox) return; if (method === 'promptpay') { detailsBox.innerHTML = ` <div class="qr-container"> <p style="font-size:0.85rem; color:#aaa;">สแกน QR Code ด้วยแอปธนาคารใดก็ได้</p> <img src="https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=00020101021229370016A000000677010111011300668123456785802TH5303764" alt="PromptPay QR"> <div style="font-size:0.8rem; font-weight:700; color:var(--primary-red);">บัญชี: BABYMONSTER FASHION SHOP</div> </div> `; } else if (method === 'mobilebanking') { detailsBox.innerHTML = ` <p style="font-size:0.85rem; color:#aaa; margin-bottom:8px;">ระบบจะส่งคำขอไปยังแอปพลิเคชัน Mobile Banking ของคุณ</p> <div style="display:flex; justify-content:center; gap:10px; font-size:1.2rem; color:#00f2fe;"> <i class="fa-solid fa-mobile-screen"></i> K PLUS / SCB EASY / Krungthai NEXT </div> `; } else if (method === 'card') { detailsBox.innerHTML = ` <div style="display:flex; flex-direction:column; gap:8px; text-align:left;"> <input type="text" placeholder="หมายเลขบัตร 16 หลัก" style="padding:8px; background:#222; border:1px solid #444; border-radius:4px;"> <div style="display:flex; gap:8px;"> <input type="text" placeholder="MM/YY" style="width:50%; padding:8px; background:#222; border:1px solid #444; border-radius:4px;"> <input type="password" placeholder="CVV" style="width:50%; padding:8px; background:#222; border:1px solid #444; border-radius:4px;"> </div> </div> `; } else { detailsBox.innerHTML = ` <div style="font-size:0.9rem; color:#10b981;"> <i class="fa-solid fa-truck"></i> ชำระเงินสดกับพนักงานขนส่งเมื่อได้รับสินค้าหน้าบ้าน </div> `; } } handleOrderSubmit(event) { event.preventDefault(); const name = document.getElementById('ship-name').value; const phone = document.getElementById('ship-phone').value; const address = document.getElementById('ship-address').value; const subtotal = this.cart.reduce((sum, item) => sum + (item.price * item.qty), 0); let shipping = subtotal >= 1500 ? 0 : 60; let discount = 0; if (this.appliedCoupon) { if (this.appliedCoupon.discountType === 'percent') { discount = (subtotal * this.appliedCoupon.value) / 100; } else if (this.appliedCoupon.discountType === 'fixed') { discount = this.appliedCoupon.value; } else if (this.appliedCoupon.discountType === 'shipping') { shipping = 0; } } const grandTotal = Math.max(0, subtotal - discount + shipping); const newOrder = { id: `ORD-2026-${Math.floor(1000 + Math.random() * 9000)}`, date: new Date().toLocaleDateString('th-TH', { year: 'numeric', month: 'short', day: 'numeric' }), status: 'confirmed', statusText: 'ยืนยันคำสั่งซื้อสำเร็จ', trackingNo: `TH26${Math.floor(10000000 + Math.random() * 90000000)}BM`, courier: 'KERRY EXPRESS', items: [...this.cart], total: grandTotal, shippingInfo: { name, phone, address } }; this.orders.unshift(newOrder); localStorage.setItem('bm_orders', JSON.stringify(this.orders)); // Clear Cart this.cart = []; this.appliedCoupon = null; this.saveCart(); this.updateHeaderBadges(); this.closeCheckout(); this.showOrderSuccessModal(newOrder); } showOrderSuccessModal(order) { const modal = document.getElementById('order-success-modal'); const receiptCard = document.getElementById('receipt-card-details'); receiptCard.innerHTML = ` <div style="text-align:left; font-size:0.88rem; background:#111; padding:16px; border-radius:8px; border:1px solid #333; margin-top:12px;"> <div style="display:flex; justify-content:space-between; margin-bottom:8px;"> <span>เลขที่คำสั่งซื้อ:</span> <strong>${order.id}</strong> </div> <div style="display:flex; justify-content:space-between; margin-bottom:8px;"> <span>เลขพัสดุติดตาม:</span> <strong style="color:var(--primary-red);">${order.trackingNo}</strong> </div> <div style="display:flex; justify-content:space-between; margin-bottom:8px;"> <span>ยอดรวมสุทธิ:</span> <strong style="font-size:1.1rem; color:var(--primary-red);">฿${order.total.toLocaleString()}</strong> </div> </div> `; modal.classList.remove('hide'); } closeOrderSuccess() { document.getElementById('order-success-modal')?.classList.add('hide'); } // Member & Profile Modal openUserProfile(activeTab = 'orders') { const modal = document.getElementById('user-modal'); const authTabs = document.getElementById('auth-tabs'); const loginView = document.getElementById('login-view'); const registerView = document.getElementById('register-view'); const dashboardView = document.getElementById('profile-dashboard-view'); if (this.currentUser.isLoggedIn) { authTabs.classList.add('hide'); loginView.classList.add('hide'); registerView.classList.add('hide'); dashboardView.classList.remove('hide'); document.getElementById('profile-display-name').textContent = this.currentUser.name; document.getElementById('profile-display-email').textContent = this.currentUser.email; this.switchDashTab(activeTab); } else { authTabs.classList.remove('hide'); dashboardView.classList.add('hide'); this.switchAuthTab('login'); } modal?.classList.remove('hide'); } closeUserModal() { document.getElementById('user-modal')?.classList.add('hide'); } switchAuthTab(tab) { document.getElementById('tab-login-btn').classList.toggle('active', tab === 'login'); document.getElementById('tab-register-btn').classList.toggle('active', tab === 'register'); document.getElementById('login-view').classList.toggle('hide', tab !== 'login'); document.getElementById('register-view').classList.toggle('hide', tab !== 'register'); } switchDashTab(tab) { document.querySelectorAll('.dash-tab').forEach(t => t.classList.remove('active')); document.querySelector(`.dash-tab[data-dash="${tab}"]`)?.classList.add('active'); const ordersPanel = document.getElementById('dash-orders-panel'); const wishlistPanel = document.getElementById('dash-wishlist-panel'); if (tab === 'orders') { ordersPanel?.classList.remove('hide'); wishlistPanel?.classList.add('hide'); this.renderOrdersTimeline(); } else { ordersPanel?.classList.add('hide'); wishlistPanel?.classList.remove('hide'); this.renderWishlistGrid(); } } renderOrdersTimeline() { const container = document.getElementById('dash-orders-panel'); const ordersCount = document.getElementById('user-orders-count'); if (!container) return; if (ordersCount) ordersCount.textContent = this.orders.length; if (this.orders.length === 0) { container.innerHTML = `<div class="empty-state"><p>ยังไม่มีประวัติการสั่งซื้อ</p></div>`; return; } container.innerHTML = this.orders.map(ord => ` <div class="order-track-card"> <div class="order-track-header"> <div> <strong>คำสั่งซื้อ #${ord.id}</strong> <div style="font-size:0.75rem; color:#aaa;">วันที่สั่งซื้อ: ${ord.date}</div> </div> <div style="text-align:right;"> <span class="badge badge-danger">${ord.statusText}</span> <div style="font-size:0.8rem; font-weight:700; color:var(--primary-red); margin-top:4px;">฿${ord.total.toLocaleString()}</div> </div> </div> <div class="order-timeline"> <div class="step-item ${ord.status === 'confirmed' || ord.status === 'preparing' || ord.status === 'shipping' || ord.status === 'delivered' ? 'active' : ''}"> <div class="step-icon"><i class="fa-solid fa-check"></i></div> <span>ยืนยันออเดอร์</span> </div> <div class="step-item ${ord.status === 'preparing' || ord.status === 'shipping' || ord.status === 'delivered' ? 'active' : ''}"> <div class="step-icon"><i class="fa-solid fa-box-open"></i></div> <span>เตรียมพัสดุ</span> </div> <div class="step-item ${ord.status === 'shipping' || ord.status === 'delivered' ? 'active' : ''}"> <div class="step-icon"><i class="fa-solid fa-truck-fast"></i></div> <span>กำลังจัดส่ง</span> </div> <div class="step-item ${ord.status === 'delivered' ? 'active' : ''}"> <div class="step-icon"><i class="fa-solid fa-house-chimney-check"></i></div> <span>จัดส่งสำเร็จ</span> </div> </div> <div style="font-size:0.82rem; background:#111; padding:8px 12px; border-radius:6px; display:flex; justify-content:space-between; align-items:center;"> <span><i class="fa-solid fa-barcode"></i> เลขติดตามพัสดุ (${ord.courier}): <strong>${ord.trackingNo}</strong></span> <button class="btn btn-outline btn-sm" onclick="app.showToast('📋 คัดลอกเลขพัสดุ ${ord.trackingNo} แล้ว!')">คัดลอก</button> </div> </div> `).join(''); } renderWishlistGrid() { const container = document.getElementById('dash-wishlist-panel'); const wishCount = document.getElementById('user-wishlist-count'); if (!container) return; const wishProducts = this.products.filter(p => this.wishlist.includes(p.id)); if (wishCount) wishCount.textContent = wishProducts.length; if (wishProducts.length === 0) { container.innerHTML = `<div class="empty-state"><p>ไม่มีสินค้าในรายการโปรด</p></div>`; return; } container.innerHTML = `<div class="product-grid">${wishProducts.map(p => this.createProductCardHTML(p)).join('')}</div>`; } handleLogin(event) { event.preventDefault(); const email = document.getElementById('login-email').value; this.currentUser = { isLoggedIn: true, name: email.split('@')[0].toUpperCase(), email: email, phone: '0812345678' }; localStorage.setItem('bm_user', JSON.stringify(this.currentUser)); this.updateHeaderBadges(); this.showToast('✅ เข้าสู่ระบบสำเร็จ ยินดีต้อนรับ!'); this.openUserProfile('orders'); } handleRegister(event) { event.preventDefault(); const name = document.getElementById('reg-name').value; const email = document.getElementById('reg-email').value; const phone = document.getElementById('reg-phone').value; this.currentUser = { isLoggedIn: true, name, email, phone }; localStorage.setItem('bm_user', JSON.stringify(this.currentUser)); this.updateHeaderBadges(); this.showToast('🎉 สมัครสมาชิกเรียบร้อย ยินดีต้อนรับ!'); this.openUserProfile('orders'); } handleLogout() { this.currentUser = { isLoggedIn: false, name: '', email: '', phone: '' }; localStorage.removeItem('bm_user'); this.updateHeaderBadges(); this.closeUserModal(); this.showToast('ออกจากระบบแล้ว'); } // Live Store Chat Assistant toggleChat(show = null) { const chatBox = document.getElementById('chat-box'); if (!chatBox) return; if (show === null) { chatBox.classList.toggle('hide'); } else if (show) { chatBox.classList.remove('hide'); } else { chatBox.classList.add('hide'); } } openChat() { this.toggleChat(true); } handleSendChatMessage(event) { event.preventDefault(); const input = document.getElementById('chat-input'); const container = document.getElementById('chat-messages-container'); if (!input || !container) return; const text = input.value.trim(); if (!text) return; // Append User Message container.innerHTML += ` <div class="chat-msg user-msg"> <div class="msg-bubble">${text}</div> </div> `; input.value = ''; container.scrollTop = container.scrollHeight; // Auto Bot Reply setTimeout(() => { let botReply = 'ขอบคุณสำหรับข้อความค่ะ! แอดมินกำลังตรวจสอบข้อมูลให้สักครู่นะคะ 💕'; const lower = text.toLowerCase(); if (lower.includes('ไซส์') || lower.includes('ขนาด')) { botReply = 'เสื้อผ้าคอลเลกชัน BABYMONSTER ออกแบบเป็นทรง Oversized แนะนำให้เลือกไซส์ตรงกับรอบอกตามตาราง หรือบวก 1 ไซส์หากชอบทรงหลวมๆ ค่ะ'; } else if (lower.includes('ส่ง') || lower.includes('จัดส่ง')) { botReply = 'ทางร้านจัดส่งด่วนผ่าน Kerry Express ภายใน 1-2 วันทำการ ช็อปครบ 1,500 บาท ส่งฟรีทั่วไทยค่ะ!'; } else if (lower.includes('ของแท้') || lower.includes('ลิขสิทธิ์')) { botReply = 'สินค้าในเว็บไซต์ BABYMONSTER Fashion Shop เป็นสินค้าลิขสิทธิ์แท้ 100% รับประกันคุณภาพคืนเงินเต็มจำนวนค่ะ'; } container.innerHTML += ` <div class="chat-msg admin-msg"> <div class="msg-bubble">${botReply}</div> </div> `; container.scrollTop = container.scrollHeight; }, 800); } // Newsletter Footer handleNewsletter(event) { event.preventDefault(); this.showToast('💌 ขอบคุณสำหรับการติดตาม! โค้ดส่วนลด 15% ถูกส่งไปยังอีเมลแล้ว'); event.target.reset(); } // Toast Popup System showToast(message) { const container = document.getElementById('toast-container'); if (!container) return; const toast = document.createElement('div'); toast.className = 'toast'; toast.innerHTML = `<i class="fa-solid fa-circle-check text-danger"></i> <span>${message}</span>`; container.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(100%)'; setTimeout(() => toast.remove(), 300); }, 3000); } // Notifications Center Drawer toggleNotificationsDrawer(show = true) { const drawer = document.getElementById('notifications-drawer'); const container = document.getElementById('notifications-list-container'); if (!drawer) return; if (show) { if (container) { container.innerHTML = NOTIFICATIONS_DATA.map(n => ` <div class="notification-item"> <h5>${n.title}</h5> <p>${n.text}</p> <small><i class="fa-regular fa-clock"></i> ${n.time}</small> </div> `).join(''); } drawer.classList.remove('hide'); } else { drawer.classList.add('hide'); } } // Admin Portal Management openAdminModal() { const modal = document.getElementById('admin-modal'); if (!modal) return; this.switchAdminTab('overview'); modal.classList.remove('hide'); } closeAdminModal() { document.getElementById('admin-modal')?.classList.add('hide'); } switchAdminTab(tab) { document.querySelectorAll('.admin-tab-btn').forEach(b => b.classList.remove('active')); document.querySelector(`.admin-tab-btn[data-tab="${tab}"]`)?.classList.add('active'); document.querySelectorAll('.admin-panel').forEach(p => p.classList.add('hide')); if (tab === 'overview') { document.getElementById('admin-overview-panel')?.classList.remove('hide'); this.renderAdminOverview(); } else if (tab === 'products') { document.getElementById('admin-products-panel')?.classList.remove('hide'); this.renderAdminProductsTable(); } else if (tab === 'orders') { document.getElementById('admin-orders-panel')?.classList.remove('hide'); this.renderAdminOrdersTable(); } else if (tab === 'coupons') { document.getElementById('admin-coupons-panel')?.classList.remove('hide'); } } renderAdminOverview() { const totalSales = this.orders.reduce((sum, o) => sum + o.total, 48900); const totalStock = this.products.reduce((sum, p) => sum + p.stock, 0); document.getElementById('admin-stat-sales').textContent = `฿${totalSales.toLocaleString()}`; document.getElementById('admin-stat-orders').textContent = `${this.orders.length + 17} รายการ`; document.getElementById('admin-stat-stock').textContent = `${totalStock} ชิ้น`; } renderAdminProductsTable() { const tbody = document.getElementById('admin-products-tbody'); if (!tbody) return; tbody.innerHTML = this.products.map(p => ` <tr> <td><img src="${p.image}"></td> <td><strong>${p.name}</strong></td> <td><span class="badge">${p.categoryName}</span></td> <td style="color:var(--primary-red); font-weight:800;">฿${p.price.toLocaleString()}</td> <td>${p.stock} ชิ้น</td> <td> <button class="btn btn-outline btn-sm" onclick="app.openEditProductModal('${p.id}')"><i class="fa-solid fa-pen"></i> แก้ไข</button> <button class="btn btn-dark btn-sm text-danger" onclick="app.deleteProduct('${p.id}')"><i class="fa-solid fa-trash"></i> ลบ</button> </td> </tr> `).join(''); } openAddProductForm() { document.getElementById('product-form-title').innerHTML = '<i class="fa-solid fa-box"></i> เพิ่มสินค้าใหม่'; document.getElementById('prod-form-id').value = ''; document.getElementById('admin-product-form').reset(); document.getElementById('admin-product-form-modal')?.classList.remove('hide'); } openEditProductModal(productId) { const prod = this.products.find(p => p.id === productId); if (!prod) return; document.getElementById('product-form-title').innerHTML = '<i class="fa-solid fa-pen"></i> แก้ไขข้อมูลสินค้า'; document.getElementById('prod-form-id').value = prod.id; document.getElementById('prod-form-name').value = prod.name; document.getElementById('prod-form-cat').value = prod.category; document.getElementById('prod-form-price').value = prod.price; document.getElementById('prod-form-orig-price').value = prod.originalPrice || ''; document.getElementById('prod-form-stock').value = prod.stock; document.getElementById('prod-form-img').value = prod.image; document.getElementById('prod-form-desc').value = prod.description || ''; document.getElementById('admin-product-form-modal')?.classList.remove('hide'); } handleSaveProduct(event) { event.preventDefault(); const id = document.getElementById('prod-form-id').value; const name = document.getElementById('prod-form-name').value; const cat = document.getElementById('prod-form-cat').value; const price = parseFloat(document.getElementById('prod-form-price').value); const origPrice = parseFloat(document.getElementById('prod-form-orig-price').value) || null; const stock = parseInt(document.getElementById('prod-form-stock').value); const img = document.getElementById('prod-form-img').value; const desc = document.getElementById('prod-form-desc').value; const catNameMap = { hoodie: 'ฮู้ดดี้', tshirt: 'เสื้อยืด', jacket: 'แจ็กเก็ต', pants: 'กางเกง', cap: 'หมวก', bag: 'กระเป๋า', accessory: 'เครื่องประดับ' }; if (id) { // Update Existing Product const prod = this.products.find(p => p.id === id); if (prod) { prod.name = name; prod.category = cat; prod.categoryName = catNameMap[cat] || cat; prod.price = price; prod.originalPrice = origPrice; prod.stock = stock; prod.image = img; prod.description = desc; } this.showToast('✅ แก้ไขข้อมูลสินค้าเรียบร้อย'); } else { // Add New Product const newProd = { id: `bm-${Math.floor(100 + Math.random() * 900)}`, name, category: cat, categoryName: catNameMap[cat] || cat, price, originalPrice: origPrice, rating: 5.0, reviewsCount: 1, isFlashSale: false, isBestSeller: false, isNewArrival: true, colors: ['Black', 'Red', 'White'], sizes: ['S', 'M', 'L', 'XL'], stock, image: img, gallery: [img], videoUrl: 'https://assets.mixkit.co/videos/preview/mixkit-fashion-model-in-a-black-outfit-41584-large.mp4', description: desc }; this.products.unshift(newProd); this.filteredProducts = [...this.products]; this.showToast('🎉 เพิ่มสินค้าใหม่เรียบร้อยแล้ว'); } document.getElementById('admin-product-form-modal')?.classList.add('hide'); this.renderAdminProductsTable(); this.renderAll(); } deleteProduct(productId) { if (confirm('คุณต้องการลบสินค้านี้ใช่หรือไม่?')) { const idx = this.products.findIndex(p => p.id === productId); if (idx > -1) { this.products.splice(idx, 1); this.filteredProducts = [...this.products]; this.renderAdminProductsTable(); this.renderAll(); this.showToast('🗑️ ลบสินค้าออกจากระบบแล้ว'); } } } renderAdminOrdersTable() { const tbody = document.getElementById('admin-orders-tbody'); if (!tbody) return; tbody.innerHTML = this.orders.map((ord, idx) => ` <tr> <td><strong>${ord.id}</strong></td> <td>${ord.date}</td> <td>${ord.shippingInfo?.name || 'ลูกค้าสมาชิก'}</td> <td style="color:var(--primary-red); font-weight:800;">฿${ord.total.toLocaleString()}</td> <td><span class="badge badge-danger">${ord.statusText}</span></td> <td> <select class="custom-select" style="font-size:0.8rem; padding:4px;" onchange="app.updateOrderStatus(${idx}, this.value)"> <option value="confirmed" ${ord.status === 'confirmed' ? 'selected' : ''}>รอชำระเงิน / ยืนยัน</option> <option value="preparing" ${ord.status === 'preparing' ? 'selected' : ''}>กำลังเตรียมสินค้า</option> <option value="shipping" ${ord.status === 'shipping' ? 'selected' : ''}>จัดส่งสินค้าแล้ว</option> <option value="delivered" ${ord.status === 'delivered' ? 'selected' : ''}>จัดส่งสำเร็จ</option> </select> </td> </tr> `).join(''); } updateOrderStatus(orderIndex, newStatus) { if (this.orders[orderIndex]) { const statusMap = { confirmed: 'รอชำระเงิน / ยืนยัน', preparing: 'กำลังเตรียมสินค้า', shipping: 'จัดส่งสินค้าแล้ว', delivered: 'จัดส่งสำเร็จ' }; this.orders[orderIndex].status = newStatus; this.orders[orderIndex].statusText = statusMap[newStatus]; localStorage.setItem('bm_orders', JSON.stringify(this.orders)); this.showToast(`🚚 อัปเดตสถานะคำสั่งซื้อ #${this.orders[orderIndex].id} เป็น "${statusMap[newStatus]}" เรียบร้อย`); this.renderAdminOrdersTable(); } } } // Global App Instance const app = new BabyMonsterShopApp();
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.3 |
proxy
|
phpinfo
|
Settings