Source

index.js

/**
 * @description external api provied demo data for testing or populating the UI
 * @name client
 * @global
 * @constant
 *
 *
 */

const client = contentful.createClient({
  space: "tzsry6lrietp",
  accessToken:
    "45db9d587d237f603aa7bfddb35918bb377e6505f129200c9c1e07968be1c628",
  host: "512063bb41145bfc98748478968f3fae74b98d9fa305d00c3920a669599985e0"
});
// console.log(client);

// variables
/**
 * @description  assigning div.cart-btn DOM to cartBtn object
 * @name cartBtn
 * @constant
 * @global
 * @type {Object}
 */
const cartBtn = document.querySelector(".cart-btn");
/**
 * @description  assigning  div.close-cart DOM to closeCartBtn object
 * @name closeCartBtn
 * @constant
 * @global
 * @type {Object}
 */
const closeCartBtn = document.querySelector(".close-cart");

/**
 * @description  assigning button.clear-cart DOM to clearBtn object
 * @name clearBtn
 * @constant
 * @global
 * @type {Object}
 */
const clearBtn = document.querySelector(".clear-cart");

/**
 * @description  assigning div.cart DOM to cartDOM object
 * @name cartDOM
 * @constant
 * @global
 * @type {Object}
 */
const cartDOM = document.querySelector(".cart");
/**
 * @description  assigning  div.overlay DOM to cartOverlay object
 * @name cartOverlay
 * @constant
 * @global
 * @type {Object}
 */
const cartOverlay = document.querySelector(".cart-overlay");
/**
 * @description  assigning  div.cart-items html DOM element to cartItems object
 * @name cartItems
 * @constant
 * @global
 * @type {Object[]}
 */

const cartItems = document.querySelector(".cart-items");
/**
 * @description  assigning  span.cart-total html DOM element to cartTotal object
 * @name cartTotal
 * @constant
 * @global
 * @type {Object}
 */
const cartTotal = document.querySelector(".cart-total");
/**
 * @description  assigning  div.cart-content html DOM element to cartContent object
 * @name cartContent
 * @constant
 * @global
 * @type {Object}
 */
const cartContent = document.querySelector(".cart-content");
/**
 * @description  assigning  div.products-center html DOM element to productsDOM object
 * @name productsDOM
 * @constant
 * @global
 * @type {Object}
 */
const productsDOM = document.querySelector(".products-center");
/**
 * @description array of {@link cartItem} objects represents products was added to cart
 * @name cart
 * @type {Array.<cartItem>}
 *
 */
let cart = [];
/**
 * @description array of {@link buttons} objects represents buttons.btn-bag
 *  - note  "those buttons created and populated into DOM using javascript"
 * @name buttonsDOM
 * @type {Array.<Object>}  DOM element "document.querySelectorAll(".bag-btn")"
 *
 */
let buttonsDOM = [];
// getting the products
/**
 * @description class responsible about fetching and destructuring items(products)
 *  then  assigning them to an array of product object
 * @class
 * @name Products
 * @example const products = new Products();
 */
class Products {
  /**
   * @description fetching and destructuring items(products)
   *  then  assigning them to an array of product object
   * @name getProducts
   * @async
   * @inner
   * @public
   * @function
   * @this Products
   * @returns [{Array.<Object>}] [array of product object]
   * @example products.getProducts();
   *
   */
  async getProducts() {
    /**
     * @description surrounding the functinality of the function in try-catch block
     *
     */
    try {
      /**
       * @await
       * @description asigning items to contentful object
       * @name contentful
       */
      let contentful = await client.getEntries({
        content_type: "comfyHouseFurrniture"
      });
      /**
       * @description testing function
       */
      console.log(contentful);
      let result = await fetch("../../products.json");
      let data = await result.json();
      /**
       * @description assigning to an array
       * @name products
       * @type {Array}
       */
      let products = contentful.items;
      // console.log(contentful);
      /**
       * @description destructuring items from the array then reassign as
       * @function
       * @name map   array higher order function
       * @param function
       * @returns {Array.<Object>}
       */
      products = products.map(item => {
        const { title, price } = item.fields;
        const { id } = item.sys;
        const image = item.fields.image.fields.file.url;
        return { title, price, id, image };
      });

      return products;
    } catch (error) {
      console.log(error);
    }
  }
}

/**
 * @description this class wraps the function that responsible for displaying {@link products} item  and adding their functionality
 * @summary DISPLAYING PRODUCTS
 * @name UI
 * @example const ui =new UI();
 * @class
 *
 */

class UI {
  /**
   * @description takes an array of {@link products} and populate the DOM
   * @see  {@link https://www.w3schools.com/js/js_htmldom.asp}
   * @this UI
   * @name displayProducts
   * @function
   * @param {Array.<Objects>}  products to be displayed
   */
  displayProducts(products) {
    let result = "";
    products.forEach(product => {
      result += `
        <!-- single product -->
        <article class="product">
          <div class="img-container">
            <img
              src=${product.image}
              class="product-img"
              alt="img product"
            />
            <button class="bag-btn" data-id=${product.id}>
              <i class="fas fa-shopping-cart"></i>
              Add to bag
            </button>
          </div>
          <h3>${product.title}</h3>
          <h4>$${product.price} </h4>
        </article>
        <!-- end of single product -->
        `;
    });

    productsDOM.innerHTML = result;
  }

  getBagButtons() {
    const buttons = [...document.querySelectorAll(".bag-btn")];
    // buttonsDOM = buttons;
    buttonsDOM = buttons;
    buttons.forEach(button => {
      let id = button.dataset.id;
      let inCart = cart.find(item => item.id === id);
      if (inCart) {
        button.textContent = "In  Cart";
        button.disabled = true;
      }
      button.addEventListener("click", event => {
        event.target.textContent = "In  Cart";
        event.target.disabled = true;
        //   get product  from products
        let cartItem = { ...Storage.getProducts(id), amount: 1 };
        console.log(cartItem);
        // add product from to the  cart
        cart = [...cart, cartItem];
        // console.log(cart);
        // save product in the local storage
        Storage.saveCart(cart);
        //   set cart values
        this.setCartValues(cart);
        // display cart items
        this.addCartItem(cartItem);
        // show the cart
        this.showCart();
      });
    });
  }
  setCartValues(cart) {
    let tempTotal = 0;
    let itemsTotal = 0;
    cart.map(item => {
      tempTotal += item.price * item.amount;
      itemsTotal += item.amount;
    });
    cartTotal.textContent = parseFloat(tempTotal.toFixed(2));
    cartItems.textContent = itemsTotal;
  }

  addCartItem(item) {
    const div = document.createElement("div");
    div.classList.add("cart-item");
    div.innerHTML = `
    <img src=${item.image} alt="" />
            <div>
              <h4> ${item.title} </h4>
              <h5>$${item.price}</h5>
              <span class="remove-item" data-id=${item.id}>remove</span>
            </div>
            <div>
              <i class="fas fa-chevron-up" data-id=${item.id}></i>
              <p class="item-amount">${item.amount}</p>
              <i class="fas fa-chevron-down" data-id=${item.id}></i>
            </div>
  
  `;
    cartContent.appendChild(div);
    // console.log(cart);
    // console.log(cartContent);
  }
  showCart() {
    cartOverlay.classList.add("transparentBcg");
    cartDOM.classList.add("showCart");
  }

  //   setup App
  setupApp() {
    cart = Storage.getCart();
    this.setCartValues(cart);
    this.populateCart(cart);
    cartBtn.addEventListener("click", this.showCart);
    closeCartBtn.addEventListener("click", this.hideCart);
  }
  populateCart(cart) {
    cart.forEach(item => this.addCartItem(item));
  }
  hideCart() {
    cartOverlay.classList.remove("transparentBcg");
    cartDOM.classList.remove("showCart");
  }
  cartLogic() {
    // clear cart button
    clearBtn.addEventListener("click", () => {
      this.clearCart();
    });
    // cart functionality
    cartContent.addEventListener("click", () => {
      if (event.target.classList.contains("remove-item")) {
        let removeItem = event.target;
        let id = removeItem.dataset.id;
        cartContent.removeChild(removeItem.parentElement.parentElement);
        this.removeItem(id);
      } else if (event.target.classList.contains("fa-chevron-up")) {
        let addAmount = event.target;
        let id = addAmount.dataset.id;
        // console.log(cart);
        let tempItem = cart.find(item => item.id === id);
        tempItem.amount = tempItem.amount + 1;
        Storage.saveCart(cart);
        this.setCartValues(cart);
        addAmount.nextElementSibling.innerText = tempItem.amount;
      } else if (event.target.classList.contains("fa-chevron-down")) {
        let lowerAmount = event.target;
        let id = lowerAmount.dataset.id;
        // console.log(cart);
        let tempItem = cart.find(item => item.id === id);
        tempItem.amount = tempItem.amount - 1;
        if (tempItem.amount > 0) {
          Storage.saveCart(cart);
          this.setCartValues(cart);
          lowerAmount.previousElementSibling.innerText = tempItem.amount;
        } else {
          cartContent.removeChild(lowerAmount.parentElement.parentElement);
          this.removeItem(id);
        }
      }
    });
  }
  clearCart() {
    let cartItems = cart.map(item => item.id);
    cartItems.forEach(id => this.removeItem(id));
    while (cartContent.children.length > 0) {
      cartContent.removeChild(cartContent.children[0]);
    }
  }
  removeItem(id) {
    cart = cart.filter(item => item.id !== id);
    this.setCartValues(cart);
    Storage.saveCart(cart);
    let button = this.getSingleButton(id);
    button.disabled = false;
    button.innerHTML = "<i class='fas fa-shopping-cart'></i> Add to cart";
  }
  getSingleButton(id) {
    return buttonsDOM.find(button => button.dataset.id === id);
  }
}

/**
 * @description class cares about adding and removing product in and from the browser local storage
 * contains main four functions
 *                              1- function  {@link saveProducts} saves {@link products} into the browser local storage
 *                              2- function  {@link getProducts} reads {@link products} from  the browser local storage
 *                              3- function  {@link saveCart} saves {@link cart} item into the browser local storage
 *                              4- function  {@link getCart} reads products from the browser local storage
 * @name Storage
 * @class
 * @example  const strage = new Storage();
 * @summary BROWSER LOCAL STORAGE   CREATING/READING OPERATIONS
 */

class Storage {
  /**
   * @description saving products in the browser localt storage takes products array as a parameter
   * {@link products}
   * @method
   * @static
   * @inner
   * @name saveProducts
   * @this Storage
   * @param {Array.<Object>}  pruducts to be saved
   * @example Storage.saveProducts(products);
   */
  static saveProducts(products) {
    localStorage.setItem("products", JSON.stringify(products));
  }
  /**
   * @description reading products from  the browser localt storage takes the id of product as a parameter
   * and returns product matches that array
   * @method
   * @static
   * @inner
   * @this Storage
   * @name getProducts
   * @param {number}  id of item to be fetched
   * @example Storage.getProducts(1);
   * @returns {Object}
   */
  static getProducts(id) {
    let products = JSON.parse(localStorage.getItem("products"));
    return products.find(product => product.id === id);
  }
  /**
   * @description saving  cartItems array in  the browser local storage takes the {@link cart}  items array as a parameter
   * @method
   * @static
   * @inner
   * @this Storage
   * @name saveCart
   * @param {Array<Object>}  cart items {@link cartItems} to be saved
   * @example Storage.saveCart(cart)
   */
  static saveCart(cart) {
    localStorage.setItem("cart", JSON.stringify(cart));
  }
  /**
   * @description reading (fetching)  cartItems   array {@link cart} from  the browser local storage
   * @method
   * @static
   * @inner
   * @name getCart
   * @this Storage
   * @param {Array<Object>}
   * @returns {Array.<Object>}  cart
   * @example Storage.getCart()
   */
  static getCart() {
    return localStorage.getItem("cart")
      ? JSON.parse(localStorage.getItem("cart"))
      : [];
  }
}

document.addEventListener("DOMContentLoaded", () => {
  const ui = new UI();
  const products = new Products();
  // set up methods
  ui.setupApp();
  //   get all prooducts
  products
    .getProducts()
    .then(products => {
      ui.displayProducts(products);
      Storage.saveProducts(products);
    })
    .then(() => {
      ui.getBagButtons();
      ui.cartLogic();
    });
});