const CartManager = {
init() {
this.state = {
isLoading: false,
lastUpdate: null,
error: null
};
this.loadCart();
this.setupEventListeners();
this.updateCartCount();
},
destroy() {
this.removeEventListeners();
this.clearTimers();
},
loadCart() {
try {
State.cart = Storage.load(CONFIG.STORAGE_KEYS.CART) || [];
} catch (error) {
console.error('Error loading cart:', error);
State.cart = [];
}
},
handleCheckoutClick() {
// 1. ตรวจสอบข้อมูลลูกค้า
if (!CustomerManager.hasRequiredInfo()) {
NotificationManager.showInfo('กรุณากรอกข้อมูลส่วนตัวก่อนสั่งซื้อ');
CustomerManager.showProfileEditor();
return;
}
// 2. ซ่อน modal ตะกร้า
document.getElementById('cartModal').classList.remove('active');
// 3. แสดง modal checkout
const checkoutModal = document.getElementById('checkoutModal');
if (checkoutModal) {
// กรอกข้อมูลลูกค้าอัตโนมัติ
const customerInfo = CustomerManager.getCustomerInfo();
document.getElementById('name').value = customerInfo.name;
document.getElementById('phone').value = customerInfo.phone;
document.getElementById('address').value = customerInfo.address;
checkoutModal.classList.add('active');
}
// 4. ผูก event submit form
const checkoutForm = document.getElementById('checkoutForm');
if (checkoutForm) {
checkoutForm.removeEventListener('submit', this.boundHandleCheckoutSubmit);
this.boundHandleCheckoutSubmit = this.handleCheckoutSubmit.bind(this);
checkoutForm.addEventListener('submit', this.boundHandleCheckoutSubmit);
}
},
// เพิ่มเมธอดสำหรับ cleanup
removeEventListeners() {
this.cartBtn?.removeEventListener('click', this.boundShowCart);
document.removeEventListener('click', this.boundHandleModalEvents);
document.removeEventListener('click', this.boundHandleCartItemEvents);
// ลบ event listener ของ checkout form
const checkoutForm = document.getElementById('checkoutForm');
if (checkoutForm) {
checkoutForm.removeEventListener('submit', this.boundHandleCheckoutSubmit);
}
},
setupEventListeners() {
this.removeEventListeners();
this.boundShowCart = this.showCart.bind(this);
this.boundHandleModalEvents = this.handleModalEvents.bind(this);
this.boundHandleCartItemEvents = this.handleCartItemEvents.bind(this);
this.boundHandleCheckoutClick = this.handleCheckoutClick.bind(this); // เพิ่มใหม่
this.cartBtn = document.getElementById('cartBtn');
this.cartBtn?.addEventListener('click', this.boundShowCart);
// เพิ่ม event listener สำหรับปุ่ม checkout
document.addEventListener('click', (e) => {
const checkoutBtn = e.target.closest('#checkoutBtn');
if (checkoutBtn && !checkoutBtn.disabled) {
this.handleCheckoutClick();
}
});
document.addEventListener('click', this.boundHandleModalEvents);
document.addEventListener('click', this.boundHandleCartItemEvents);
},
handleModalEvents(e) {
if (e.target.matches('.modal-close')) {
const modal = e.target.closest('.modal');
if (modal) this.closeModal(modal);
}
},
addToCart(product) {
if (!product || !product.id) {
console.error('Invalid product', product);
return;
}
try {
const existingItem = State.cart.find(item => item.id === product.id);
if (existingItem) {
// ตรวจสอบสต็อกสินค้า
if (existingItem.quantity >= product.stock) {
NotificationManager.showError('ขออภัย สินค้าในสต็อกไม่เพียงพอ');
return;
}
existingItem.quantity += 1;
} else {
State.cart.push({
id: product.id,
name: product.name,
price: product.price,
image: product.image,
quantity: 1,
stock: product.stock
});
}
this.updateCartCount();
this.saveCart();
this.animateCartButton();
NotificationManager.showSuccess(`เพิ่ม${product.name}ลงตะกร้าแล้ว`);
} catch (error) {
console.error('Error adding to cart:', error);
NotificationManager.showError('เกิดข้อผิดพลาดในการเพิ่มสินค้า');
}
},
updateQuantity(productId, change) {
const item = State.cart.find(item => item.id === productId);
if (!item) return;
const newQuantity = item.quantity + change;
if (newQuantity <= 0) {
this.removeFromCart(productId);
return;
}
if (newQuantity > item.stock) {
NotificationManager.showError('ขออภัย สินค้าในสต็อกไม่เพียงพอ');
return;
}
item.quantity = newQuantity;
this.updateCartCount();
this.saveCart();
this.showCart();
},
removeFromCart(productId) {
State.cart = State.cart.filter(item => item.id !== productId);
this.updateCartCount();
this.saveCart();
this.showCart();
NotificationManager.showInfo('ลบสินค้าออกจากตะกร้าแล้ว');
},
updateCartCount() {
const count = State.cart.reduce((sum, item) => sum + item.quantity, 0);
const cartCount = document.querySelector('.cart-count');
if (cartCount) {
cartCount.textContent = count;
cartCount.classList.toggle('hidden', count === 0);
if (count > 0) {
cartCount.classList.add('bounce');
setTimeout(() => cartCount.classList.remove('bounce'), 300);
}
}
},
saveCart() {
Storage.save(CONFIG.STORAGE_KEYS.CART, State.cart);
},
handleCartItemEvents(e) {
const button = e.target.closest('button');
if (!button) return;
const action = button.dataset.action;
const productId = button.closest('.cart-item')?.dataset.productId;
if (!productId) return;
try {
switch (action) {
case 'increase':
this.updateQuantity(productId, 1);
break;
case 'decrease':
this.updateQuantity(productId, -1);
break;
case 'remove':
this.confirmRemoveItem(productId);
break;
}
} catch (error) {
this.handleCartError(error);
}
},
async validateCheckout() {
if (!CustomerManager.hasRequiredInfo()) {
NotificationManager.showInfo('กรุณากรอกข้อมูลส่วนตัวก่อนสั่งซื้อ');
await CustomerManager.showProfileEditor();
return false;
}
const {subtotal} = this.getCartTotal();
if (subtotal < CONFIG.MIN_ORDER_AMOUNT) {
NotificationManager.showError(
`ยอดสั่งซื้อขั้นต่ำ ${Utils.formatPrice(CONFIG.MIN_ORDER_AMOUNT)}`
);
return false;
}
return true;
},
getCartItems() {
return State.cart.map(item => ({
id: item.id,
name: item.name,
price: item.price,
quantity: item.quantity,
total: item.price * item.quantity
}));
},
getCartTotal() {
const subtotal = State.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
const deliveryFee = subtotal >= CONFIG.FREE_DELIVERY_AMOUNT ? 0 : CONFIG.DELIVERY_FEE;
return {
subtotal,
deliveryFee,
total: subtotal + deliveryFee
};
},
updateCartSummary() {
const {subtotal, deliveryFee, total} = this.getCartTotal();
document.getElementById('subtotal').textContent = Utils.formatPrice(subtotal);
document.getElementById('deliveryFee').textContent = Utils.formatPrice(deliveryFee);
document.getElementById('total').textContent = Utils.formatPrice(total);
// อัพเดทปุ่ม checkout
const checkoutBtn = document.getElementById('checkoutBtn');
if (checkoutBtn) {
const isMinimumMet = subtotal >= CONFIG.MIN_ORDER_AMOUNT;
checkoutBtn.disabled = !isMinimumMet;
if (!isMinimumMet) {
checkoutBtn.title = `ขั้นต่ำ ${Utils.formatPrice(CONFIG.MIN_ORDER_AMOUNT)}`;
document.getElementById('minimumOrderWarning').textContent =
`* ยอดสั่งซื้อขั้นต่ำ ${Utils.formatPrice(CONFIG.MIN_ORDER_AMOUNT)}`;
} else {
checkoutBtn.title = '';
document.getElementById('minimumOrderWarning').textContent = '';
}
}
},
clearCart() {
State.cart = [];
this.updateCartCount();
this.saveCart();
},
animateCartButton() {
const button = document.getElementById('cartBtn');
if (button) {
button.classList.add('bounce');
setTimeout(() => button.classList.remove('bounce'), 300);
}
},
createCartItemElement(item) {
const element = document.createElement('div');
element.className = 'cart-item';
element.dataset.productId = item.id;
// หาข้อมูลสินค้าจาก State.products
const product = State.products.find(p => p.id === item.id);
const inStock = product && product.stock > item.quantity;
element.innerHTML = `
<div class="cart-item-image">
<img src="${item.image}" alt="${item.name}" loading="lazy">
</div>
<div class="cart-item-content">
<div class="cart-item-header">
<h4 class="cart-item-title">${item.name}</h4>
<button class="remove-btn" data-action="remove" title="ลบสินค้า">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="cart-item-details">
<div class="cart-item-price">
<span class="price-label">ราคา:</span>
<span class="price-value">${Utils.formatPrice(item.price * item.quantity)}</span>
</div>
<div class="cart-item-controls">
<button class="quantity-btn"
data-action="decrease"
title="ลดจำนวน">
<i class="fas fa-minus"></i>
</button>
<span class="quantity">${item.quantity}</span>
<button class="quantity-btn"
data-action="increase"
title="เพิ่มจำนวน"
${!inStock ? 'disabled' : ''}>
<i class="fas fa-plus"></i>
</button>
</div>
</div>
${!inStock ? `
<div class="stock-warning">
<i class="fas fa-exclamation-triangle"></i>
สินค้าในสต็อกไม่เพียงพอ
</div>
` : ''}
</div>
`;
// เพิ่ม event listeners สำหรับปุ่มควบคุม
const controls = element.querySelector('.cart-item-controls');
if (controls) {
controls.addEventListener('click', (e) => {
const button = e.target.closest('button');
if (!button) return;
const action = button.dataset.action;
if (action === 'increase') {
this.updateQuantity(item.id, 1);
} else if (action === 'decrease') {
this.updateQuantity(item.id, -1);
}
});
}
// เพิ่ม event listener สำหรับปุ่มลบ
const removeBtn = element.querySelector('.remove-btn');
if (removeBtn) {
removeBtn.addEventListener('click', () => {
// เพิ่ม animation ก่อนลบ
element.classList.add('removing');
setTimeout(() => {
this.removeFromCart(item.id);
}, 300);
});
}
return element;
},
showCart() {
const modal = document.getElementById('cartModal');
if (!modal) return;
const cartItems = document.getElementById('cartItems');
if (!cartItems) return;
cartItems.innerHTML = '';
if (State.cart.length === 0) {
cartItems.innerHTML = `
<div class="empty-cart">
<i class="fas fa-shopping-cart"></i>
<p>ตะกร้าว่างเปล่า</p>
<button class="btn btn-primary continue-shopping">
<i class="fas fa-arrow-left"></i> เลือกซื้อสินค้าต่อ
</button>
</div>
`;
// เพิ่ม event listener สำหรับปุ่มเลือกซื้อสินค้าต่อ
const continueBtn = cartItems.querySelector('.continue-shopping');
if (continueBtn) {
continueBtn.addEventListener('click', () => {
modal.classList.remove('active');
});
}
} else {
// สร้าง elements สำหรับแต่ละสินค้าในตะกร้า
State.cart.forEach(item => {
const itemElement = this.createCartItemElement(item);
cartItems.appendChild(itemElement);
});
// เพิ่มส่วนสรุปราคา
const summaryElement = document.createElement('div');
summaryElement.className = 'cart-summary';
const {subtotal, deliveryFee, total} = this.getCartTotal();
summaryElement.innerHTML = `
<div class="cart-subtotal">
<span>ราคาสินค้า:</span>
<span id="subtotal">${Utils.formatPrice(subtotal)}</span>
</div>
<div class="cart-delivery">
<span>ค่าจัดส่ง:</span>
<span id="deliveryFee">${Utils.formatPrice(deliveryFee)}</span>
</div>
<div class="cart-total">
<span>รวมทั้งสิ้น:</span>
<span id="total">${Utils.formatPrice(total)}</span>
</div>
<div id="minimumOrderWarning" class="minimum-warning"></div>
<button id="checkoutBtn" class="checkout-btn"
${subtotal < CONFIG.MIN_ORDER_AMOUNT ? 'disabled' : ''}>
<i class="fas fa-shopping-cart"></i>
สั่งซื้อสินค้า
</button>
`;
cartItems.appendChild(summaryElement);
}
modal.classList.add('active');
},
async handleCheckoutSubmit(e) {
e.preventDefault();
try {
Utils.showLoading();
// 1. ตรวจสอบข้อมูลลูกค้า
if (!CustomerManager.hasRequiredInfo()) {
Utils.hideLoading();
NotificationManager.showInfo('กรุณากรอกข้อมูลส่วนตัวก่อนสั่งซื้อ');
CustomerManager.showProfileEditor();
return;
}
// 2. สร้างข้อมูลการสั่งซื้อ
const orderData = {
id: Utils.generateOrderId(),
customer: CustomerManager.getCustomerInfo(),
items: this.getCartItems(),
...this.getCartTotal(),
paymentMethod: e.target.payment.value,
status: 'pending',
createdAt: new Date().toISOString()
};
// 3. ดำเนินการชำระเงิน
const paymentResult = await PaymentManager.processPayment({
...orderData,
total: orderData.total,
});
if (paymentResult.success) {
// 4. บันทึกข้อมูลการสั่งซื้อ
this.saveOrder(orderData, paymentResult);
// 5. ส่งการแจ้งเตือน
await NotificationService.sendOrderNotification({
...orderData,
paymentResult
});
// 6. เคลียร์ตะกร้าและปิด modal
this.clearCart();
document.getElementById('checkoutModal').classList.remove('active');
}
} catch (error) {
console.error('Checkout error:', error);
NotificationManager.showError(error.message || 'เกิดข้อผิดพลาดในการสั่งซื้อ');
} finally {
Utils.hideLoading();
}
},
saveOrder(orderData, paymentResult) {
try {
// บันทึกข้อมูลการสั่งซื้อ
const orders = Storage.load(CONFIG.STORAGE_KEYS.ORDERS) || [];
orders.push({
...orderData,
payment: {
method: paymentResult.method,
reference: paymentResult.data.reference,
status: 'pending'
}
});
Storage.save(CONFIG.STORAGE_KEYS.ORDERS, orders);
// บันทึกในประวัติการสั่งซื้อของลูกค้า
State.customer.orderHistory.push(orderData.id);
CustomerManager.saveCustomerData();
} catch (error) {
console.error('Error saving order:', error);
throw new Error('ไม่สามารถบันทึกข้อมูลการสั่งซื้อได้');
}
}
};