File manager - Edit - /home/webapp69.cm.in.th/u69319090003/shop003/script.js
Back
/* ========================================================================== CLOTHDEE CLIENT-SIDE REACTIVE STATE ENGINE Author: Kasama & Antigravity Pair Programmer ========================================================================== */ document.addEventListener('DOMContentLoaded', () => { // ==================== 1. DEFAULT DATA PRE-POPULATION ==================== const DEFAULT_PRODUCTS = [ { id: 'prod_1', name: 'Classic Knit Cardigan', price: 1490, category: 'jacket', sizes: ['S', 'M', 'L', 'XL'], desc: 'Soft merino wool knitwear with tortoise shell buttons, ribbed cuffs, and a cozy loose-fit silhouette.', image: 'https://images.unsplash.com/photo-1620799140408-edc6dcb6d633?w=500&auto=format&fit=crop&q=60', seller: 'admin', stock: 14, sold: 45 }, { id: 'prod_2', name: 'Minimalist Oversized Tee', price: 590, category: 'tshirt', sizes: ['S', 'M', 'L', 'XL', 'XXL'], desc: '100% premium organic cotton. Features a relaxed silhouette, dropped shoulders, and a clean crew neck.', image: 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?w=500&auto=format&fit=crop&q=60', seller: 'admin', stock: 8, sold: 28 }, { id: 'prod_3', name: 'Premium Linen Trouser', price: 1290, category: 'shirt', sizes: ['M', 'L', 'XL'], desc: 'Breathable linen blend. Designed with a tailored straight leg, elasticated drawstring waistband, and side pockets.', image: 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=500&auto=format&fit=crop&q=60', seller: 'admin', stock: 5, sold: 12 }, { id: 'prod_4', name: 'Denim Heavyweight Jacket', price: 790, category: 'jacket', sizes: ['S', 'M', 'L', 'XL'], desc: 'Heavy denim jacket with custom wash, double breast pocket design, and premium buttons.', image: 'https://images.unsplash.com/photo-1576995853123-5a10305d93c0?w=500&auto=format&fit=crop&q=60', seller: 'seller_shop', stock: 10, sold: 63 }, { id: 'prod_5', name: 'Pastel Cozy Hoodie', price: 490, category: 'hoodie', sizes: ['M', 'L', 'XL', 'XXL'], desc: 'Soft pastel cotton hoodie with drop shoulder aesthetic and front pouch pocket.', image: 'https://images.unsplash.com/photo-1556821840-3a63f95609a7?w=500&auto=format&fit=crop&q=60', seller: 'kasamaxd', stock: 12, sold: 94 } ]; // Initialize LocalStorage Collections const existingProducts = localStorage.getItem('products'); if (!existingProducts || !existingProducts.includes('stock')) { localStorage.setItem('products', JSON.stringify(DEFAULT_PRODUCTS)); } if (!localStorage.getItem('users')) { // Pre-configure Admin Account const defaultUsers = [ { username: 'admin', password: '123456', role: 'admin' }, { username: 'kasamaxd', password: '111', role: 'user' }, { username: 'seller_shop', password: '222', role: 'user' } ]; localStorage.setItem('users', JSON.stringify(defaultUsers)); } if (!localStorage.getItem('cart')) { localStorage.setItem('cart', JSON.stringify([])); } // ==================== 2. APPLICATION STATE ==================== let state = { currentUser: JSON.parse(sessionStorage.getItem('currentUser')) || null, products: JSON.parse(localStorage.getItem('products')), cart: JSON.parse(localStorage.getItem('cart')), searchQuery: '', selectedCategory: 'all', selectedSizeFilter: '', selectedDetailProduct: null, uploadedImageBase64: null }; // Save state helper const saveCartToStorage = () => { localStorage.setItem('cart', JSON.stringify(state.cart)); updateCartUI(); }; const saveProductsToStorage = () => { localStorage.setItem('products', JSON.stringify(state.products)); renderProducts(); }; // ==================== 3. DOM ELEMENT SELECTORS ==================== // Auth Headers elements const authStatusContainer = document.getElementById('authStatusContainer'); const btnShowLogin = document.getElementById('btnShowLogin'); const btnShowRegister = document.getElementById('btnShowRegister'); // Modals const loginModal = document.getElementById('loginModal'); const registerModal = document.getElementById('registerModal'); const addProductModal = document.getElementById('addProductModal'); const productDetailModal = document.getElementById('productDetailModal'); const cartModal = document.getElementById('cartModal'); // Forms const loginForm = document.getElementById('loginForm'); const registerForm = document.getElementById('registerForm'); const addProductForm = document.getElementById('addProductForm'); // Filters const searchInput = document.getElementById('searchInput'); const searchBtn = document.getElementById('searchBtn'); const logoReload = document.getElementById('logoReload'); const categoryMenu = document.getElementById('categoryMenu'); const sizeFilterGrid = document.getElementById('sizeFilterGrid'); // Products shelf grid const productsGridContainer = document.getElementById('productsGridContainer'); const noProductsFound = document.getElementById('noProductsFound'); // Cart counters and drawers const cartCount = document.getElementById('cartCount'); const cartTrigger = document.getElementById('cartTrigger'); const dropdownCartItems = document.getElementById('dropdownCartItems'); const dropdownTotal = document.getElementById('dropdownTotal'); const btnGoCart = document.getElementById('btnGoCart'); const cartItemsList = document.getElementById('cartItemsList'); const cartTotalItemsCount = document.getElementById('cartTotalItemsCount'); const cartGrandTotal = document.getElementById('cartGrandTotal'); const btnCheckoutSubmit = document.getElementById('btnCheckoutSubmit'); // Product Detail Modal elements const detailModalImg = document.getElementById('detailModalImg'); const detailModalCategory = document.getElementById('detailModalCategory'); const detailModalTitle = document.getElementById('detailModalTitle'); const detailModalSeller = document.getElementById('detailModalSeller'); const detailModalPrice = document.getElementById('detailModalPrice'); const detailModalDesc = document.getElementById('detailModalDesc'); const detailModalSizeOptions = document.getElementById('detailModalSizeOptions'); const btnDetailModalAddToCart = document.getElementById('btnDetailModalAddToCart'); // Image Upload Elements const prodImageFile = document.getElementById('prodImageFile'); const imageUploaderWrapper = document.getElementById('imageUploaderWrapper'); const uploaderPlaceholder = document.getElementById('uploaderPlaceholder'); const uploadPreviewImage = document.getElementById('uploadPreviewImage'); const btnRemovePreviewImage = document.getElementById('btnRemovePreviewImage'); // Toast Box const toastBox = document.getElementById('toastBox'); // ==================== 4. TOAST ALERTS BANNERS ==================== const showToast = (message, type = 'success') => { const toast = document.createElement('div'); toast.className = `toast-item ${type}`; let iconClass = 'ti-circle-check-filled'; if (type === 'danger') iconClass = 'ti-circle-x-filled'; else if (type === 'warning') iconClass = 'ti-alert-circle-filled'; toast.innerHTML = ` <i class="ti ${iconClass}"></i> <span>${message}</span> `; toastBox.appendChild(toast); // Auto remove toast setTimeout(() => { toast.style.animation = 'slideInToast 0.3s cubic-bezier(0.16, 1, 0.3, 1) reverse forwards'; setTimeout(() => { toast.remove(); }, 300); }, 3000); }; // ==================== 5. AUTHENTICATION OPERATIONS ==================== const updateAuthUI = () => { if (state.currentUser) { // Logged in header updates let roleBadge = state.currentUser.role === 'admin' ? '<span style="color: #fef08a; font-weight: 700; margin-left:4px;">(แอดมิน)</span>' : ''; authStatusContainer.innerHTML = ` <div class="user-badge-header"> <i class="ti ti-user-circle"></i> <span>${state.currentUser.username}</span> ${roleBadge} </div> <span class="divider-thin">|</span> <button class="btn-text-link" id="btnLogoutAction">ออกจากระบบ</button> `; // Add listener to logout document.getElementById('btnLogoutAction').addEventListener('click', logoutUser); } else { // Guest header updates authStatusContainer.innerHTML = ` <button class="btn-text-link" id="btnShowLogin">เข้าสู่ระบบ</button> <span class="divider-thin">/</span> <button class="btn-text-link" id="btnShowRegister">สมัครสมาชิก</button> `; // Add listeners to show modal buttons document.getElementById('btnShowLogin').addEventListener('click', () => openModal(loginModal)); document.getElementById('btnShowRegister').addEventListener('click', () => openModal(registerModal)); } renderProducts(); // Refresh buttons view (delete button appears based on role) }; const logoutUser = () => { sessionStorage.removeItem('currentUser'); state.currentUser = null; updateAuthUI(); showToast('ออกจากระบบเรียบร้อยแล้ว'); }; // Login logic loginForm.addEventListener('submit', (e) => { e.preventDefault(); const usernameInput = document.getElementById('loginUsername').value.trim(); const passwordInput = document.getElementById('loginPassword').value; // Load users from DB const users = JSON.parse(localStorage.getItem('users')); const foundUser = users.find(u => u.username === usernameInput && u.password === passwordInput); if (foundUser) { state.currentUser = { username: foundUser.username, role: foundUser.role }; sessionStorage.setItem('currentUser', JSON.stringify(state.currentUser)); closeModal(loginModal); loginForm.reset(); updateAuthUI(); showToast(`ยินดีต้อนรับกลับมา! คุณ ${state.currentUser.username}`); } else { showToast('ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง', 'danger'); } }); // Register logic registerForm.addEventListener('submit', (e) => { e.preventDefault(); const usernameInput = document.getElementById('regUsername').value.trim(); const passwordInput = document.getElementById('regPassword').value; if (usernameInput.toLowerCase() === 'admin') { showToast('ไม่สามารถใช้ชื่อผู้ใช้ admin ได้', 'danger'); return; } if (passwordInput.length < 4) { showToast('รหัสผ่านต้องมีความยาว 4 ตัวอักษรขึ้นไป', 'danger'); return; } // Load users from DB const users = JSON.parse(localStorage.getItem('users')); const alreadyExists = users.some(u => u.username.toLowerCase() === usernameInput.toLowerCase()); if (alreadyExists) { showToast('ชื่อผู้ใช้นี้มีคนใช้งานแล้วในระบบ', 'danger'); return; } // Add user const newUser = { username: usernameInput, password: passwordInput, role: 'user' }; users.push(newUser); localStorage.setItem('users', JSON.stringify(users)); showToast('สมัครสมาชิกผู้ขายสำเร็จแล้ว! กรุณาเข้าสู่ระบบ'); closeModal(registerModal); openModal(loginModal); registerForm.reset(); }); // ==================== 6. MODAL WINDOW CONTROLLERS ==================== const openModal = (modalElement) => { modalElement.classList.add('active'); document.body.style.overflow = 'hidden'; }; const closeModal = (modalElement) => { modalElement.classList.remove('active'); document.body.style.overflow = ''; }; // Close button triggers document.querySelectorAll('.modal-close, .btn-cancel, .btn-continue-shopping').forEach(btn => { btn.addEventListener('click', (e) => { const modalId = btn.getAttribute('data-modal') || btn.closest('.modal-overlay').id; closeModal(document.getElementById(modalId)); }); }); // Close on overlay backdrop click document.querySelectorAll('.modal-overlay').forEach(overlay => { overlay.addEventListener('click', (e) => { if (e.target === overlay) { closeModal(overlay); } }); }); // ==================== 7. PRODUCTS CATALOG OPERATIONS ==================== // Category menu click filters categoryMenu.querySelectorAll('.btn-tab').forEach(item => { item.addEventListener('click', () => { categoryMenu.querySelectorAll('.btn-tab').forEach(btn => btn.classList.remove('active')); item.classList.add('active'); state.selectedCategory = item.getAttribute('data-category'); renderProducts(); }); }); // Size filter grid click filters sizeFilterGrid.querySelectorAll('.btn-size-tab').forEach(btn => { btn.addEventListener('click', () => { if (btn.classList.contains('active')) { btn.classList.remove('active'); state.selectedSizeFilter = ''; } else { sizeFilterGrid.querySelectorAll('.btn-size-tab').forEach(b => b.classList.remove('active')); btn.classList.add('active'); state.selectedSizeFilter = btn.getAttribute('data-size'); } renderProducts(); }); }); // Search bar submit const searchIcon = document.querySelector('.search-icon'); if (searchIcon) { searchIcon.addEventListener('click', () => { state.searchQuery = searchInput.value.trim(); renderProducts(); }); } if (searchBtn) { searchBtn.addEventListener('click', () => { state.searchQuery = searchInput.value.trim(); renderProducts(); }); } searchInput.addEventListener('input', () => { state.searchQuery = searchInput.value.trim(); renderProducts(); }); searchInput.addEventListener('keyup', (e) => { if (e.key === 'Enter') { state.searchQuery = searchInput.value.trim(); renderProducts(); } }); logoReload.addEventListener('click', (e) => { e.preventDefault(); searchInput.value = ''; state.searchQuery = ''; state.selectedCategory = 'all'; state.selectedSizeFilter = ''; categoryMenu.querySelectorAll('.btn-tab').forEach(btn => btn.classList.remove('active')); categoryMenu.querySelector('[data-category="all"]').classList.add('active'); sizeFilterGrid.querySelectorAll('.btn-size-tab').forEach(b => b.classList.remove('active')); renderProducts(); }); // RENDER PRODUCTS TO GRID SHELF const renderProducts = () => { // Filter logic let filtered = state.products; // Search match if (state.searchQuery) { const query = state.searchQuery.toLowerCase(); const categoryThaiMap = { tshirt: 'เสื้อยืด t-shirt tshirt', shirt: 'เสื้อเชิ้ต shirt', jacket: 'แจ็คเก็ต jacket เสื้อคลุม', hoodie: 'เสื้อฮู้ด hoodie สเวตเตอร์' }; filtered = filtered.filter(p => { const nameMatch = p.name.toLowerCase().includes(query); const descMatch = (p.desc || '').toLowerCase().includes(query); const catMatch = (categoryThaiMap[p.category] || p.category || '').toLowerCase().includes(query); const sizeMatch = (p.sizes || []).some(s => s.toLowerCase() === query); return nameMatch || descMatch || catMatch || sizeMatch; }); } // Category match if (state.selectedCategory !== 'all') { filtered = filtered.filter(p => p.category === state.selectedCategory); } // Size match if (state.selectedSizeFilter) { filtered = filtered.filter(p => p.sizes.includes(state.selectedSizeFilter)); } // In case there are no items if (filtered.length === 0) { productsGridContainer.innerHTML = ''; noProductsFound.classList.remove('hidden'); return; } noProductsFound.classList.add('hidden'); // Draw items productsGridContainer.innerHTML = filtered.map(prod => { // Check delete button visibility // admin can delete anything, normal user can delete only their own uploaded products const canDelete = state.currentUser && ( state.currentUser.role === 'admin' || state.currentUser.username === prod.seller ); const deleteBtnHtml = canDelete ? `<button class="btn-card-delete" onclick="window.deleteProductItem('${prod.id}')" title="ลบสินค้านี้"><i class="ti ti-trash"></i></button>` : ''; const sizeOptionsHtml = prod.sizes.map(s => `<option value="${s}">${s}</option>`).join(''); const stockVal = prod.stock !== undefined ? prod.stock : 10; return ` <div class="product-card-item" id="card-${prod.id}"> <div class="prod-card-img-box" onclick="window.viewProductDetail('${prod.id}')" style="cursor:pointer;"> <img class="prod-card-img" src="${prod.image}" alt="${prod.name}"> <span class="card-badge-left">New</span> </div> <div class="prod-card-details"> <h3 class="prod-card-title" onclick="window.viewProductDetail('${prod.id}')" style="cursor:pointer;">${prod.name}</h3> <p class="prod-card-desc">${prod.desc || ''}</p> <div class="prod-card-selectors"> <div class="selector-group"> <label for="size-select-${prod.id}">ไซส์</label> <select class="prod-card-select" id="size-select-${prod.id}"> ${sizeOptionsHtml} </select> </div> <div class="selector-group"> <label for="qty-input-${prod.id}">จำนวน</label> <input type="number" class="prod-card-input" id="qty-input-${prod.id}" value="1" min="1" max="${stockVal}"> </div> </div> <div class="prod-card-price-sold-row"> <div class="price-stock-info"> <div class="prod-card-price">฿${prod.price.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}</div> <div class="prod-card-stock">คงเหลือ: ${stockVal} ชิ้น</div> </div> <div class="card-actions-strip"> <button class="btn-card-quickview" onclick="window.viewProductDetail('${prod.id}')" title="ดูรายละเอียด"><i class="ti ti-eye"></i></button> <button class="btn-card-add-to-cart" onclick="window.quickAddToCart('${prod.id}')"> <i class="ti ti-shopping-cart"></i> ใส่ตะกร้า </button> ${deleteBtnHtml} </div> </div> </div> </div> `; }).join(''); }; // ==================== 8. IMAGE UPLOAD PREVIEW & BASE64 ==================== prodImageFile.addEventListener('change', (e) => { const file = e.target.files[0]; if (!file) return; // Size check (max 5MB just to be safe with local storage bounds) if (file.size > 5 * 1024 * 1024) { showToast('ไฟล์ภาพขนาดใหญ่เกิน 5MB กรุณาเลือกไฟล์ภาพที่เล็กลง', 'danger'); prodImageFile.value = ''; return; } const reader = new FileReader(); reader.onload = (event) => { state.uploadedImageBase64 = event.target.result; // Show preview uploadPreviewImage.src = event.target.result; uploadPreviewImage.classList.remove('hidden'); uploaderPlaceholder.classList.add('hidden'); btnRemovePreviewImage.classList.remove('hidden'); }; reader.readAsDataURL(file); }); const resetImageUploadField = () => { state.uploadedImageBase64 = null; prodImageFile.value = ''; uploadPreviewImage.src = ''; uploadPreviewImage.classList.add('hidden'); uploaderPlaceholder.classList.remove('hidden'); btnRemovePreviewImage.classList.add('hidden'); }; btnRemovePreviewImage.addEventListener('click', (e) => { e.stopPropagation(); // Stop trigger file selection click resetImageUploadField(); }); // ==================== 9. PRODUCT UPLOAD ACTION ==================== addProductForm.addEventListener('submit', (e) => { e.preventDefault(); if (!state.currentUser) { showToast('กรุณาลงชื่อเข้าใช้ก่อนเพิ่มสินค้า', 'danger'); return; } if (!state.uploadedImageBase64) { showToast('กรุณาอัปโหลดรูปภาพเสื้อผ้าสินค้าด้วยครับ', 'danger'); return; } const name = document.getElementById('prodName').value.trim(); const price = parseInt(document.getElementById('prodPrice').value); const category = document.getElementById('prodCategory').value; const desc = document.getElementById('prodDesc').value.trim(); // Get size check boxes const checkedSizesElements = document.querySelectorAll('input[name="prodSizes"]:checked'); if (checkedSizesElements.length === 0) { showToast('กรุณาระบุไซส์เสื้ออย่างน้อย 1 ไซส์เพื่อจัดขาย', 'danger'); return; } const sizes = Array.from(checkedSizesElements).map(el => el.value); // Create new item const newProduct = { id: 'prod_' + Date.now(), name: name, price: price, category: category, sizes: sizes, desc: desc, image: state.uploadedImageBase64, seller: state.currentUser.username, sold: 0 }; try { state.products.unshift(newProduct); saveProductsToStorage(); closeModal(addProductModal); // Reset form fields addProductForm.reset(); resetImageUploadField(); showToast('ลงรูปภาพและบันทึกสินค้าใหม่เรียบร้อยแล้ว!'); } catch (error) { // If local storage quota gets full showToast('บันทึกล้มเหลว! (LocalStorage โควต้าเต็มเนื่องจากไฟล์รูปภาพใหญ่)', 'danger'); state.products.shift(); // remove item } }); // ==================== 10. PRODUCT DETAIL MODAL QUICK-VIEW ==================== window.viewProductDetail = (productId) => { const prod = state.products.find(p => p.id === productId); if (!prod) return; state.selectedDetailProduct = prod; // Map Category label let catLabel = 'เสื้อผ้า'; if (prod.category === 'tshirt') catLabel = 'เสื้อยืด'; else if (prod.category === 'shirt') catLabel = 'เสื้อเชิ้ต'; else if (prod.category === 'jacket') catLabel = 'แจ็คเก็ต & คลุม'; else if (prod.category === 'hoodie') catLabel = 'เสื้อฮู้ด'; // Render info detailModalImg.src = prod.image; detailModalCategory.textContent = catLabel; detailModalTitle.textContent = prod.name; detailModalSeller.textContent = prod.seller; detailModalPrice.textContent = prod.price; detailModalDesc.textContent = prod.desc; // Render Size selection pills detailModalSizeOptions.innerHTML = prod.sizes.map((s, idx) => { // Select first size by default const activeClass = idx === 0 ? 'active' : ''; return `<button class="detail-size-pill ${activeClass}" onclick="window.selectDetailSize(this, '${s}')">${s}</button>`; }).join(''); // Set default selected size as the first item state.selectedDetailProduct.chosenSize = prod.sizes[0]; openModal(productDetailModal); }; window.selectDetailSize = (pillElement, size) => { detailModalSizeOptions.querySelectorAll('.detail-size-pill').forEach(p => p.classList.remove('active')); pillElement.classList.add('active'); if (state.selectedDetailProduct) { state.selectedDetailProduct.chosenSize = size; } }; // Add to cart from detail modal btnDetailModalAddToCart.addEventListener('click', () => { if (!state.selectedDetailProduct) return; const prod = state.selectedDetailProduct; const chosenSize = prod.chosenSize; if (!chosenSize) { showToast('กรุณาเลือกไซส์เสื้อก่อนเพิ่มลงตะกร้า', 'warning'); return; } // Add to cart state logic // Item uniqueness in cart is determined by: id + chosenSize const cartItemIdx = state.cart.findIndex(item => item.id === prod.id && item.size === chosenSize); if (cartItemIdx > -1) { state.cart[cartItemIdx].quantity += 1; } else { state.cart.push({ id: prod.id, name: prod.name, price: prod.price, size: chosenSize, image: prod.image, quantity: 1 }); } saveCartToStorage(); closeModal(productDetailModal); showToast(`เพิ่มเสื้อยืดไซส์ ${chosenSize} ลงตะกร้าแล้ว`); }); // ==================== 11. CART & CHECKOUT MANAGEMENT ==================== // Update Cart numbers and dropdown components const updateCartUI = () => { // Grand totals calculation const totalItems = state.cart.reduce((sum, item) => sum + item.quantity, 0); const totalPrice = state.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); // Update header count badge cartCount.textContent = totalItems; // 1. Draw Mini Hover Cart Dropdown list if (state.cart.length === 0) { dropdownCartItems.innerHTML = '<div class="empty-dropdown">ไม่มีสินค้าในตะกร้า</div>'; dropdownTotal.textContent = '฿0'; } else { dropdownCartItems.innerHTML = state.cart.slice(-3).map(item => ` <div class="dropdown-item"> <img class="drop-item-img" src="${item.image}" alt="${item.name}"> <div class="drop-item-info"> <div class="drop-item-title">${item.name}</div> <div class="drop-item-meta">ไซส์: ${item.size} x ${item.quantity} ชิ้น</div> </div> <div class="drop-item-price">฿${item.price}</div> </div> `).join(''); dropdownTotal.textContent = `฿${totalPrice}`; } // 2. Draw Main Cart Modal window list cartTotalItemsCount.textContent = `${totalItems} ชิ้น`; cartGrandTotal.textContent = `฿${totalPrice}`; if (state.cart.length === 0) { cartItemsList.innerHTML = ` <div class="empty-cart-view"> <i class="ti ti-shopping-cart-x"></i> <p>ไม่มีสินค้าที่เลือกซื้ออยู่ในขณะนี้ ลองไปหาเสื้อผ้าที่ชอบดูนะ</p> </div> `; btnCheckoutSubmit.disabled = true; btnCheckoutSubmit.style.opacity = 0.5; btnCheckoutSubmit.style.cursor = 'not-allowed'; } else { btnCheckoutSubmit.disabled = false; btnCheckoutSubmit.style.opacity = 1; btnCheckoutSubmit.style.cursor = 'pointer'; cartItemsList.innerHTML = state.cart.map((item, idx) => ` <div class="cart-row-item"> <img class="cart-item-img" src="${item.image}" alt="${item.name}"> <div class="cart-item-details"> <h4 class="cart-item-title">${item.name}</h4> <span class="cart-item-size-badge">ไซส์เสื้อ: ${item.size}</span> <span class="cart-item-price-unit">ชิ้นละ ฿${item.price}</span> </div> <div class="qty-controller-box"> <button class="qty-btn" onclick="window.changeCartQty(${idx}, -1)">-</button> <div class="qty-input-box">${item.quantity}</div> <button class="qty-btn" onclick="window.changeCartQty(${idx}, 1)">+</button> </div> <div class="cart-item-subtotal">฿${item.price * item.quantity}</div> <button class="btn-cart-row-remove" onclick="window.removeCartItem(${idx})" title="ลบรายการสินค้า"><i class="ti ti-trash"></i></button> </div> `).join(''); } }; // Trigger Cart Modal drawer open cartTrigger.addEventListener('click', (e) => { // If clicking on dropdown child elements e.g. "ดูตะกร้าสินค้า" button if (e.target.closest('.dropdown-footer') || e.target.closest('.dropdown-items')) return; openModal(cartModal); }); btnGoCart.addEventListener('click', () => { openModal(cartModal); }); // Global window functions for template script triggers window.quickAddToCart = (productId) => { const prod = state.products.find(p => p.id === productId); if (!prod) return; const sizeSelect = document.getElementById(`size-select-${productId}`); const chosenSize = sizeSelect ? sizeSelect.value : (prod.sizes[0] || 'S'); const qtyInput = document.getElementById(`qty-input-${productId}`); const chosenQty = qtyInput ? (parseInt(qtyInput.value) || 1) : 1; const cartItemIdx = state.cart.findIndex(item => item.id === prod.id && item.size === chosenSize); if (cartItemIdx > -1) { state.cart[cartItemIdx].quantity += chosenQty; } else { state.cart.push({ id: prod.id, name: prod.name, price: prod.price, size: chosenSize, image: prod.image, quantity: chosenQty }); } saveCartToStorage(); showToast(`เพิ่ม "${prod.name}" (ไซส์ ${chosenSize}) จำนวน ${chosenQty} ชิ้น ลงในตะกร้าแล้ว`); openModal(cartModal); }; window.changeCartQty = (index, delta) => { const item = state.cart[index]; if (!item) return; item.quantity += delta; if (item.quantity <= 0) { // Remove item state.cart.splice(index, 1); showToast('ลบรายการสินค้าออกจากตะกร้าแล้ว'); } saveCartToStorage(); }; window.removeCartItem = (index) => { if (state.cart[index]) { state.cart.splice(index, 1); saveCartToStorage(); showToast('ลบสินค้าออกจากตะกร้าเรียบร้อยแล้ว'); } }; // Checkout submit action btnCheckoutSubmit.addEventListener('click', () => { if (state.cart.length === 0) return; showToast('สั่งซื้อสินค้าเรียบร้อยแล้ว! ขอบคุณที่เลือกใช้บริการ ClothDee', 'success'); state.cart = []; saveCartToStorage(); closeModal(cartModal); }); // ==================== 12. ADMIN/OWNER PRODUCT DELETION ==================== window.deleteProductItem = (productId) => { const index = state.products.findIndex(p => p.id === productId); if (index === -1) return; const prodNameShort = state.products[index].name.substring(0, 20) + '...'; if (confirm(`คุณต้องการที่จะลบสินค้า "${state.products[index].name}" ออกจากระบบร้านค้าใช่หรือไม่? (การดำเนินการนี้ไม่สามารถย้อนกลับได้)`)) { state.products.splice(index, 1); saveProductsToStorage(); showToast(`ลบสินค้า "${prodNameShort}" เรียบร้อยแล้ว`, 'danger'); } }; // ==================== 13. INITIALIZATION ==================== updateAuthUI(); updateCartUI(); renderProducts(); });
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.36 |
proxy
|
phpinfo
|
Settings