cart.js

16.77 KB
05/11/2024 10:50
JS
cart.js
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('ไม่สามารถบันทึกข้อมูลการสั่งซื้อได้');
    }
  }
};