products.js

9.95 KB
05/11/2024 09:15
JS
products.js
const ProductManager = {
  init() {
    this.loadProducts();
    this.setupFilterButtons();
    this.setupSearch();
    this.setupCartButtons();
  },

  async loadProducts() {
    try {
      Utils.showLoading();
      const response = await fetch('data/products.json');
      if (!response.ok) throw new Error('ไม่สามารถโหลดข้อมูลสินค้าได้');

      const data = await response.json();
      State.products = data.products;
      State.categories = data.categories;
      State.tags = data.tags;

      this.renderCategories();
      this.renderProducts();
    } catch (error) {
      console.error('Error loading products:', error);
      NotificationManager.showError('ไม่สามารถโหลดข้อมูลสินค้าได้');
    } finally {
      Utils.hideLoading();
    }
  },

  setupSearch() {
    const searchInput = document.getElementById('searchInput');
    if (!searchInput) return;

    let debounceTimeout;
    searchInput.addEventListener('input', (e) => {
      clearTimeout(debounceTimeout);
      debounceTimeout = setTimeout(() => {
        const searchTerm = e.target.value.toLowerCase().trim();
        this.filterProducts('all', searchTerm);
      }, 300);
    });
  },

  setupFilterButtons() {
    const filterContainer = document.getElementById('categoryFilters');
    if (!filterContainer) return;

    filterContainer.addEventListener('click', (e) => {
      const button = e.target.closest('.category-filter');
      if (!button) return;

      const searchTerm = document.getElementById('searchInput')?.value.toLowerCase().trim() || '';

      filterContainer.querySelectorAll('.category-filter').forEach(btn => {
        btn.classList.remove('active');
      });
      button.classList.add('active');

      const category = button.dataset.category;
      this.filterProducts(category, searchTerm);
    });

    document.getElementById('productGrid')?.addEventListener('click', (e) => {
      const tag = e.target.closest('.product-tag');
      if (!tag) return;

      const tagId = tag.dataset.tag;
      if (tagId) {
        document.getElementById('searchInput').value = '';
        filterContainer.querySelectorAll('.category-filter').forEach(btn => {
          btn.classList.remove('active');
        });
        filterContainer.querySelector('[data-category="all"]').classList.add('active');
        this.filterByTag(tagId);
      }
    });
  },

  renderCategories() {
    const filterContainer = document.getElementById('categoryFilters');
    if (!filterContainer) return;

    filterContainer.innerHTML = `
      <button class="category-filter active" data-category="all">
        <i class="fas fa-th"></i> ทั้งหมด
      </button>
    `;

    State.categories
      .sort((a, b) => a.order - b.order)
      .forEach(category => {
        filterContainer.innerHTML += `
          <button class="category-filter" data-category="${category.id}">
            <i class="fas ${category.icon}"></i> ${category.name}
          </button>
        `;
      });
  },

  filterProducts(category, searchTerm = '') {
    let filteredProducts = category === 'all'
      ? [...State.products]
      : State.products.filter(p => p.category === category);

    if (searchTerm) {
      filteredProducts = filteredProducts.filter(p =>
        p.name.toLowerCase().includes(searchTerm) ||
        p.description.toLowerCase().includes(searchTerm) ||
        p.tags.some(tag => State.tags[tag]?.name.toLowerCase().includes(searchTerm))
      );
    }

    this.renderProducts(filteredProducts);
    this.updateProductCount(filteredProducts.length);
  },

  filterByTag(tagId) {
    const filteredProducts = State.products.filter(p => p.tags.includes(tagId));
    this.renderProducts(filteredProducts);
    this.updateProductCount(filteredProducts.length);
  },

  updateProductCount(count) {
    const countElement = document.getElementById('productCount');
    if (countElement) {
      countElement.textContent = `${count} รายการ`;
    }
  },

  renderProducts(products = State.products) {
    const productGrid = document.getElementById('productGrid');
    if (!productGrid) return;

    productGrid.innerHTML = '';

    if (products.length === 0) {
      productGrid.innerHTML = `
        <div class="no-products">
          <i class="fas fa-search"></i>
          <p>ไม่พบสินค้าที่คุณค้นหา</p>
        </div>
      `;
      return;
    }

    products
      .filter(product => product.available)
      .forEach(product => {
        const productElement = this.createProductCard(product);
        productGrid.appendChild(productElement);
      });
  },

  createProductTags(tags) {
    return tags
      .map(tag => {
        const tagInfo = State.tags[tag];
        if (!tagInfo) return '';

        return `
          <span class="product-tag"
                data-tag="${tag}"
                style="background-color: ${tagInfo.color}">
            <i class="fas ${tagInfo.icon}"></i>
            ${tagInfo.name}
          </span>
        `;
      })
      .join('');
  },

  createNutritionInfo(nutrition) {
    return `
      <div class="nutrition-info">
        <div class="nutrition-item">
          <span class="label">แคลอรี่</span>
          <span class="value">${nutrition.calories}</span>
        </div>
        <div class="nutrition-item">
          <span class="label">โปรตีน</span>
          <span class="value">${nutrition.protein}g</span>
        </div>
        <div class="nutrition-item">
          <span class="label">คาร์บ</span>
          <span class="value">${nutrition.carbs}g</span>
        </div>
        <div class="nutrition-item">
          <span class="label">ไขมัน</span>
          <span class="value">${nutrition.fat}g</span>
        </div>
      </div>
    `;
  },

  createProductCard(product) {
    const card = document.createElement('div');
    card.className = 'product-card';

    const category = State.categories.find(c => c.id === product.category);
    const inStock = product.stock > 0;

    card.innerHTML = `
      <div class="product-image-container">
        <img src="${product.image}" alt="${product.name}" loading="lazy">
        <div class="product-tags">
          ${this.createProductTags(product.tags)}
          <span class="product-tag category-tag" data-tag="${product.category}">
            <i class="fas ${category?.icon || 'fa-tag'}"></i>
            ${category?.name || 'ทั่วไป'}
          </span>
        </div>
        ${product.stock <= 10 && product.stock > 0 ?
        `<div class="stock-warning">เหลือเพียง ${product.stock} ชิ้น</div>` : ''}
      </div>
      <div class="product-content">
        <h3 class="product-title">${product.name}</h3>
        <p class="product-description">${product.description}</p>
        ${product.nutrition ? this.createNutritionInfo(product.nutrition) : ''}
        <div class="product-allergens">
          <small>แจ้งเตือนสารก่อภูมิแพ้: ${product.allergens.join(', ')}</small>
        </div>
        <div class="product-footer">
          <div class="product-price">${Utils.formatPrice(product.price)}</div>
          <button class="add-to-cart-btn"
                  data-product-id="${product.id}"
                  ${!inStock ? 'disabled' : ''}>
            <i class="fas ${inStock ? 'fa-cart-plus' : 'fa-times'}"></i>
            ${inStock ? 'เพิ่มลงตะกร้า' : 'สินค้าหมด'}
          </button>
        </div>
      </div>
    `;

    return card;
  },

  setupCartButtons() {
    document.addEventListener('click', (e) => {
      const addToCartBtn = e.target.closest('.add-to-cart-btn');
      if (!addToCartBtn) return;

      e.preventDefault();
      e.stopPropagation();

      const productId = addToCartBtn.dataset.productId;
      if (!productId) return;

      const product = State.products.find(p => p.id === productId);
      if (!product) return;

      this.addToCart(product);
    });
  },

  addToCart(product) {
    if (!product.available || product.stock <= 0) {
      NotificationManager.showError('ขออภัย สินค้าหมด');
      return;
    }

    try {
      CartManager.addToCart({
        id: product.id,
        name: product.name,
        price: product.price,
        image: product.image,
        stock: product.stock
      });

      // อัพเดทจำนวนสินค้าคงเหลือ
      const updatedProduct = State.products.find(p => p.id === product.id);
      if (updatedProduct) {
        updatedProduct.stock -= 1;
        if (updatedProduct.stock <= 0) {
          updatedProduct.available = false;
        }
        this.updateProductCard(updatedProduct);
      }

    } catch (error) {
      console.error('Error adding to cart:', error);
      NotificationManager.showError('เกิดข้อผิดพลาดในการเพิ่มสินค้าลงตะกร้า');
    }
  },
  updateProductCard(product) {
    const productCard = document.querySelector(`[data-product-id="${product.id}"]`)?.closest('.product-card');
    if (!productCard) return;

    const addToCartBtn = productCard.querySelector('.add-to-cart-btn');
    const stockWarning = productCard.querySelector('.stock-warning');

    if (product.stock <= 0) {
      addToCartBtn.disabled = true;
      addToCartBtn.innerHTML = '<i class="fas fa-times"></i> สินค้าหมด';
      if (stockWarning) stockWarning.remove();
    } else if (product.stock <= 10) {
      addToCartBtn.disabled = false;
      if (!stockWarning) {
        const warning = document.createElement('div');
        warning.className = 'stock-warning';
        warning.textContent = `เหลือเพียง ${product.stock} ชิ้น`;
        productCard.querySelector('.product-image-container').appendChild(warning);
      } else {
        stockWarning.textContent = `เหลือเพียง ${product.stock} ชิ้น`;
      }
    }
  }
};