// App JS moved from index.html
// Global variables
let cart = [];
let products = [];
const CART_KEY = 'blue_mobile_cart_v1';
// Load products from products.json
async function loadProducts() {
const loading = document.getElementById('loading');
const container = document.getElementById('productsContainer');
loading.style.display = 'block';
try {
// Fetch local JSON file (simulate API response)
const res = await fetch('./products.json', {cache: 'no-store'});
const json = await res.json();
// Support both plain array and API-style response { status, data: { products: [...] } }
if (Array.isArray(json)) {
products = json;
} else if (json && json.data && Array.isArray(json.data.products)) {
products = json.data.products;
// optional: use metadata for logging or pagination UI later
console.info('Products loaded from API-style response', {total: json.data.total || products.length, meta: json.meta || null});
} else if (json && Array.isArray(json.products)) {
products = json.products; // another possible shape
} else {
throw new Error('Unexpected products.json format');
}
// Optional small delay for UX
await new Promise(resolve => setTimeout(resolve, 500));
loading.style.display = 'none';
displayProducts();
// Load cart from localStorage if present
const saved = localStorage.getItem(CART_KEY);
if (saved) {
try {
cart = JSON.parse(saved) || [];
} catch (e) {
cart = [];
}
updateCartDisplay();
}
} catch (err) {
loading.style.display = 'none';
container.innerHTML = '<p class="text-danger">เกิดข้อผิดพลาดในการโหลดสินค้า</p>';
console.error(err);
}
}
function displayProducts() {
const container = document.getElementById('productsContainer');
container.innerHTML = '';
products.forEach(product => {
const discountPercent = Math.round(((product.originalPrice - product.price) / product.originalPrice) * 100);
// Prefer local preprocessed WebP images when available; otherwise build WebP URLs for remote images
const isLocal = !!product.image;
let webpSmall, webpLarge;
if (isLocal) {
const imgBase = product.image; // e.g. images/product-1
webpSmall = `${imgBase}-400.webp`;
webpLarge = `${imgBase}-800.webp`;
} else {
// Build responsive WebP URLs (Unsplash supports fm=webp)
const imgBase = product.image;
webpSmall = imgBase.replace(/(\?)/, '&') + '&w=400&fm=webp';
webpLarge = imgBase.replace(/(\?)/, '&') + '&w=800&fm=webp';
}
const productCard = `
<div class="col-lg-4 col-md-6 mb-4">
<div class="card h-100">
<picture>
<source type="image/webp" srcset="${webpSmall} 400w, ${webpLarge} 800w" />
<img class="card-img-top" src="${webpSmall}" srcset="${webpSmall} 400w, ${webpLarge} 800w" sizes="(max-width: 768px) 400px, 800px" alt="${product.name}" loading="lazy">
</picture>
<div class="card-body">
<h5 class="card-title">${product.name}</h5>
<p class="card-text">${product.description}</p>
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="price">${product.price.toLocaleString()}</span> บาท
<div class="original-price">${product.originalPrice.toLocaleString()} บาท</div>
<span class="badge bg-success">ลด ${discountPercent}%</span>
</div>
</div>
</div>
<div class="card-footer bg-transparent">
<button class="btn btn-gold w-100" onclick="addToCart(${product.id})">
<i class="bi bi-cart-plus"></i> เพิ่มในตะกร้า
</button>
</div>
</div>
</div>
`;
container.innerHTML += productCard;
});
}
function addToCart(productId) {
const product = products.find(p => p.id === productId);
const existingItem = cart.find(item => item.id === productId);
if (existingItem) {
existingItem.quantity += 1;
} else {
cart.push({...product, quantity: 1});
}
updateCartDisplay();
saveCart();
// Success animation
const buttons = document.querySelectorAll(`button[onclick="addToCart(${productId})"]`);
buttons.forEach(button => {
button.classList.add('success-animation');
button.innerHTML = '<i class="bi bi-check"></i> เพิ่มแล้ว';
setTimeout(() => {
button.classList.remove('success-animation');
button.innerHTML = '<i class="bi bi-cart-plus"></i> เพิ่มในตะกร้า';
}, 1000);
});
}
function updateCartDisplay() {
const cartCount = document.getElementById('cartCount');
const cartItems = document.getElementById('cartItems');
const cartTotal = document.getElementById('cartTotal');
const checkoutBtn = document.getElementById('checkoutBtn');
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
const totalPrice = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
cartCount.textContent = totalItems;
cartTotal.textContent = totalPrice.toLocaleString();
if (cart.length === 0) {
cartItems.innerHTML = '<p class="text-center text-muted">ตะกร้าสินค้าว่างเปล่า</p>';
checkoutBtn.disabled = true;
} else {
cartItems.innerHTML = cart.map(item => `
<div class="d-flex justify-content-between align-items-center mb-3 p-3 border rounded">
<div class="d-flex align-items-center">
<img src="${item.image}-400.webp" width="60" height="60" class="rounded me-3" alt="${item.name}">
<div>
<h6 class="mb-1">${item.name}</h6>
<small class="text-muted">${item.price.toLocaleString()} บาท</small>
</div>
</div>
<div class="d-flex align-items-center">
<button class="btn btn-sm btn-outline-warning me-2" onclick="changeQuantity(${item.id}, -1)">-</button>
<span class="mx-2">${item.quantity}</span>
<button class="btn btn-sm btn-outline-warning me-3" onclick="changeQuantity(${item.id}, 1)">+</button>
<button class="btn btn-sm btn-outline-danger" onclick="removeFromCart(${item.id})">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
`).join('');
checkoutBtn.disabled = false;
}
}
function changeQuantity(productId, change) {
const item = cart.find(item => item.id === productId);
if (item) {
item.quantity += change;
if (item.quantity <= 0) {
removeFromCart(productId);
} else {
updateCartDisplay();
saveCart();
}
}
}
function removeFromCart(productId) {
cart = cart.filter(item => item.id !== productId);
updateCartDisplay();
saveCart();
}
function proceedToCheckout() {
if (cart.length === 0) return;
const cartModal = bootstrap.Modal.getInstance(document.getElementById('cartModal'));
if (cartModal) cartModal.hide();
setTimeout(() => {
updateOrderSummary();
const checkoutModal = new bootstrap.Modal(document.getElementById('checkoutModal'));
checkoutModal.show();
}, 500);
}
function updateOrderSummary() {
const orderSummary = document.getElementById('orderSummary');
const orderTotal = document.getElementById('orderTotal');
const totalPrice = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const shipping = totalPrice >= 1000 ? 0 : 100; // ส่งฟรีเมื่อซื้อมากกว่า 1000 บาท
const finalTotal = totalPrice + shipping;
orderSummary.innerHTML = cart.map(item => `
<div class="d-flex justify-content-between mb-2">
<span>${item.name} x${item.quantity}</span>
<span>${(item.price * item.quantity).toLocaleString()} บาท</span>
</div>
`).join('') + `
<div class="d-flex justify-content-between mb-2">
<span>ค่าจัดส่ง</span>
<span>${shipping === 0 ? 'ฟรี' : shipping + ' บาท'}</span>
</div>
`;
orderTotal.textContent = finalTotal.toLocaleString() + ' บาท';
}
async function processPayment() {
const form = document.getElementById('checkoutForm');
const formData = new FormData(form);
// Validate required fields
if (!form.checkValidity()) {
form.reportValidity();
return;
}
// Show loading state
const paymentBtn = document.getElementById('processPaymentBtn');
const originalText = paymentBtn.innerHTML;
paymentBtn.disabled = true;
paymentBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>กำลังดำเนินการ...';
// Simulate payment processing
await new Promise(resolve => setTimeout(resolve, 3000));
// Simulate successful payment
paymentBtn.innerHTML = '<i class="bi bi-check-circle"></i> ชำระเงินสำเร็จ';
setTimeout(() => {
const checkoutModal = bootstrap.Modal.getInstance(document.getElementById('checkoutModal'));
if (checkoutModal) checkoutModal.hide();
// Show success message
alert('ชำระเงินสำเร็จ! ขอบคุณสำหรับการสั่งซื้อ เราจะจัดส่งสินค้าให้คุณในเร็วๆ นี้');
// Clear cart
cart = [];
updateCartDisplay();
saveCart();
// Reset button
paymentBtn.disabled = false;
paymentBtn.innerHTML = originalText;
}, 1500);
}
function saveCart() {
try {
localStorage.setItem(CART_KEY, JSON.stringify(cart));
} catch (e) {
console.warn('Failed to save cart', e);
}
}
function scrollToProducts() {
document.getElementById('products').scrollIntoView({behavior: 'smooth'});
}
// Payment method toggle & helpers
function initUI() {
const paymentRadios = document.querySelectorAll('input[name="payment"]');
const creditCardForm = document.getElementById('creditCardForm');
paymentRadios.forEach(radio => {
radio.addEventListener('change', function() {
if (this.value === 'credit') {
creditCardForm.style.display = 'block';
} else {
creditCardForm.style.display = 'none';
}
});
});
// Credit card number formatting
const cardNumberInput = document.querySelector('input[placeholder*="หมายเลขบัตร"]');
if (cardNumberInput) {
cardNumberInput.addEventListener('input', function() {
let value = this.value.replace(/\s+/g, '').replace(/[^0-9]/gi, '');
let formattedValue = value.match(/.{1,4}/g)?.join(' ') || value;
if (formattedValue !== this.value) {
this.value = formattedValue;
}
});
}
// Expiry date formatting
const expiryInput = document.querySelector('input[placeholder="MM/YY"]');
if (expiryInput) {
expiryInput.addEventListener('input', function() {
let value = this.value.replace(/\D/g, '');
if (value.length >= 2) {
value = value.substring(0, 2) + '/' + value.substring(2, 4);
}
this.value = value;
});
}
// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const offsetTop = target.offsetTop - 80; // Account for fixed navbar
window.scrollTo({
top: offsetTop,
behavior: 'smooth'
});
}
});
});
// Navbar scroll effect
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 50) {
navbar.style.background = 'rgba(26, 26, 26, 0.98)';
} else {
navbar.style.background = 'rgba(26, 26, 26, 0.95)';
}
});
}
// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', function() {
initUI();
loadProducts();
registerServiceWorker();
});
// Register service worker
function registerServiceWorker() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js').then(reg => {
console.info('Service worker registered', reg.scope);
}).catch(err => {
console.warn('Service worker registration failed', err);
});
}
}
// PWA install prompt handling
let deferredPrompt = null;
function setupPWAInstall() {
const installModalEl = document.getElementById('installModal');
const installConfirm = document.getElementById('installConfirm');
const installCancel = document.getElementById('installCancel');
const bsModal = installModalEl ? new bootstrap.Modal(installModalEl) : null;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
// show modal
if (bsModal) bsModal.show();
});
installConfirm && installConfirm.addEventListener('click', async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const choice = await deferredPrompt.userChoice;
deferredPrompt = null;
if (bsModal) bsModal.hide();
console.info('PWA install choice', choice);
});
installCancel && installCancel.addEventListener('click', () => {
if (bsModal) bsModal.hide();
});
window.addEventListener('appinstalled', () => {
if (bsModal) bsModal.hide();
console.info('App installed');
});
}