Source

index.js

  1. /**
  2. * @description external api provied demo data for testing or populating the UI
  3. * @name client
  4. * @global
  5. * @constant
  6. *
  7. *
  8. */
  9. const client = contentful.createClient({
  10. space: "tzsry6lrietp",
  11. accessToken:
  12. "45db9d587d237f603aa7bfddb35918bb377e6505f129200c9c1e07968be1c628",
  13. host: "512063bb41145bfc98748478968f3fae74b98d9fa305d00c3920a669599985e0"
  14. });
  15. // console.log(client);
  16. // variables
  17. /**
  18. * @description assigning div.cart-btn DOM to cartBtn object
  19. * @name cartBtn
  20. * @constant
  21. * @global
  22. * @type {Object}
  23. */
  24. const cartBtn = document.querySelector(".cart-btn");
  25. /**
  26. * @description assigning div.close-cart DOM to closeCartBtn object
  27. * @name closeCartBtn
  28. * @constant
  29. * @global
  30. * @type {Object}
  31. */
  32. const closeCartBtn = document.querySelector(".close-cart");
  33. /**
  34. * @description assigning button.clear-cart DOM to clearBtn object
  35. * @name clearBtn
  36. * @constant
  37. * @global
  38. * @type {Object}
  39. */
  40. const clearBtn = document.querySelector(".clear-cart");
  41. /**
  42. * @description assigning div.cart DOM to cartDOM object
  43. * @name cartDOM
  44. * @constant
  45. * @global
  46. * @type {Object}
  47. */
  48. const cartDOM = document.querySelector(".cart");
  49. /**
  50. * @description assigning div.overlay DOM to cartOverlay object
  51. * @name cartOverlay
  52. * @constant
  53. * @global
  54. * @type {Object}
  55. */
  56. const cartOverlay = document.querySelector(".cart-overlay");
  57. /**
  58. * @description assigning div.cart-items html DOM element to cartItems object
  59. * @name cartItems
  60. * @constant
  61. * @global
  62. * @type {Object[]}
  63. */
  64. const cartItems = document.querySelector(".cart-items");
  65. /**
  66. * @description assigning span.cart-total html DOM element to cartTotal object
  67. * @name cartTotal
  68. * @constant
  69. * @global
  70. * @type {Object}
  71. */
  72. const cartTotal = document.querySelector(".cart-total");
  73. /**
  74. * @description assigning div.cart-content html DOM element to cartContent object
  75. * @name cartContent
  76. * @constant
  77. * @global
  78. * @type {Object}
  79. */
  80. const cartContent = document.querySelector(".cart-content");
  81. /**
  82. * @description assigning div.products-center html DOM element to productsDOM object
  83. * @name productsDOM
  84. * @constant
  85. * @global
  86. * @type {Object}
  87. */
  88. const productsDOM = document.querySelector(".products-center");
  89. /**
  90. * @description array of {@link cartItem} objects represents products was added to cart
  91. * @name cart
  92. * @type {Array.<cartItem>}
  93. *
  94. */
  95. let cart = [];
  96. /**
  97. * @description array of {@link buttons} objects represents buttons.btn-bag
  98. * - note "those buttons created and populated into DOM using javascript"
  99. * @name buttonsDOM
  100. * @type {Array.<Object>} DOM element "document.querySelectorAll(".bag-btn")"
  101. *
  102. */
  103. let buttonsDOM = [];
  104. // getting the products
  105. /**
  106. * @description class responsible about fetching and destructuring items(products)
  107. * then assigning them to an array of product object
  108. * @class
  109. * @name Products
  110. * @example const products = new Products();
  111. */
  112. class Products {
  113. /**
  114. * @description fetching and destructuring items(products)
  115. * then assigning them to an array of product object
  116. * @name getProducts
  117. * @async
  118. * @inner
  119. * @public
  120. * @function
  121. * @this Products
  122. * @returns [{Array.<Object>}] [array of product object]
  123. * @example products.getProducts();
  124. *
  125. */
  126. async getProducts() {
  127. /**
  128. * @description surrounding the functinality of the function in try-catch block
  129. *
  130. */
  131. try {
  132. /**
  133. * @await
  134. * @description asigning items to contentful object
  135. * @name contentful
  136. */
  137. let contentful = await client.getEntries({
  138. content_type: "comfyHouseFurrniture"
  139. });
  140. /**
  141. * @description testing function
  142. */
  143. console.log(contentful);
  144. let result = await fetch("../../products.json");
  145. let data = await result.json();
  146. /**
  147. * @description assigning to an array
  148. * @name products
  149. * @type {Array}
  150. */
  151. let products = contentful.items;
  152. // console.log(contentful);
  153. /**
  154. * @description destructuring items from the array then reassign as
  155. * @function
  156. * @name map array higher order function
  157. * @param function
  158. * @returns {Array.<Object>}
  159. */
  160. products = products.map(item => {
  161. const { title, price } = item.fields;
  162. const { id } = item.sys;
  163. const image = item.fields.image.fields.file.url;
  164. return { title, price, id, image };
  165. });
  166. return products;
  167. } catch (error) {
  168. console.log(error);
  169. }
  170. }
  171. }
  172. /**
  173. * @description this class wraps the function that responsible for displaying {@link products} item and adding their functionality
  174. * @summary DISPLAYING PRODUCTS
  175. * @name UI
  176. * @example const ui =new UI();
  177. * @class
  178. *
  179. */
  180. class UI {
  181. /**
  182. * @description takes an array of {@link products} and populate the DOM
  183. * @see {@link https://www.w3schools.com/js/js_htmldom.asp}
  184. * @this UI
  185. * @name displayProducts
  186. * @function
  187. * @param {Array.<Objects>} products to be displayed
  188. */
  189. displayProducts(products) {
  190. let result = "";
  191. products.forEach(product => {
  192. result += `
  193. <!-- single product -->
  194. <article class="product">
  195. <div class="img-container">
  196. <img
  197. src=${product.image}
  198. class="product-img"
  199. alt="img product"
  200. />
  201. <button class="bag-btn" data-id=${product.id}>
  202. <i class="fas fa-shopping-cart"></i>
  203. Add to bag
  204. </button>
  205. </div>
  206. <h3>${product.title}</h3>
  207. <h4>$${product.price} </h4>
  208. </article>
  209. <!-- end of single product -->
  210. `;
  211. });
  212. productsDOM.innerHTML = result;
  213. }
  214. getBagButtons() {
  215. const buttons = [...document.querySelectorAll(".bag-btn")];
  216. // buttonsDOM = buttons;
  217. buttonsDOM = buttons;
  218. buttons.forEach(button => {
  219. let id = button.dataset.id;
  220. let inCart = cart.find(item => item.id === id);
  221. if (inCart) {
  222. button.textContent = "In Cart";
  223. button.disabled = true;
  224. }
  225. button.addEventListener("click", event => {
  226. event.target.textContent = "In Cart";
  227. event.target.disabled = true;
  228. // get product from products
  229. let cartItem = { ...Storage.getProducts(id), amount: 1 };
  230. console.log(cartItem);
  231. // add product from to the cart
  232. cart = [...cart, cartItem];
  233. // console.log(cart);
  234. // save product in the local storage
  235. Storage.saveCart(cart);
  236. // set cart values
  237. this.setCartValues(cart);
  238. // display cart items
  239. this.addCartItem(cartItem);
  240. // show the cart
  241. this.showCart();
  242. });
  243. });
  244. }
  245. setCartValues(cart) {
  246. let tempTotal = 0;
  247. let itemsTotal = 0;
  248. cart.map(item => {
  249. tempTotal += item.price * item.amount;
  250. itemsTotal += item.amount;
  251. });
  252. cartTotal.textContent = parseFloat(tempTotal.toFixed(2));
  253. cartItems.textContent = itemsTotal;
  254. }
  255. addCartItem(item) {
  256. const div = document.createElement("div");
  257. div.classList.add("cart-item");
  258. div.innerHTML = `
  259. <img src=${item.image} alt="" />
  260. <div>
  261. <h4> ${item.title} </h4>
  262. <h5>$${item.price}</h5>
  263. <span class="remove-item" data-id=${item.id}>remove</span>
  264. </div>
  265. <div>
  266. <i class="fas fa-chevron-up" data-id=${item.id}></i>
  267. <p class="item-amount">${item.amount}</p>
  268. <i class="fas fa-chevron-down" data-id=${item.id}></i>
  269. </div>
  270. `;
  271. cartContent.appendChild(div);
  272. // console.log(cart);
  273. // console.log(cartContent);
  274. }
  275. showCart() {
  276. cartOverlay.classList.add("transparentBcg");
  277. cartDOM.classList.add("showCart");
  278. }
  279. // setup App
  280. setupApp() {
  281. cart = Storage.getCart();
  282. this.setCartValues(cart);
  283. this.populateCart(cart);
  284. cartBtn.addEventListener("click", this.showCart);
  285. closeCartBtn.addEventListener("click", this.hideCart);
  286. }
  287. populateCart(cart) {
  288. cart.forEach(item => this.addCartItem(item));
  289. }
  290. hideCart() {
  291. cartOverlay.classList.remove("transparentBcg");
  292. cartDOM.classList.remove("showCart");
  293. }
  294. cartLogic() {
  295. // clear cart button
  296. clearBtn.addEventListener("click", () => {
  297. this.clearCart();
  298. });
  299. // cart functionality
  300. cartContent.addEventListener("click", () => {
  301. if (event.target.classList.contains("remove-item")) {
  302. let removeItem = event.target;
  303. let id = removeItem.dataset.id;
  304. cartContent.removeChild(removeItem.parentElement.parentElement);
  305. this.removeItem(id);
  306. } else if (event.target.classList.contains("fa-chevron-up")) {
  307. let addAmount = event.target;
  308. let id = addAmount.dataset.id;
  309. // console.log(cart);
  310. let tempItem = cart.find(item => item.id === id);
  311. tempItem.amount = tempItem.amount + 1;
  312. Storage.saveCart(cart);
  313. this.setCartValues(cart);
  314. addAmount.nextElementSibling.innerText = tempItem.amount;
  315. } else if (event.target.classList.contains("fa-chevron-down")) {
  316. let lowerAmount = event.target;
  317. let id = lowerAmount.dataset.id;
  318. // console.log(cart);
  319. let tempItem = cart.find(item => item.id === id);
  320. tempItem.amount = tempItem.amount - 1;
  321. if (tempItem.amount > 0) {
  322. Storage.saveCart(cart);
  323. this.setCartValues(cart);
  324. lowerAmount.previousElementSibling.innerText = tempItem.amount;
  325. } else {
  326. cartContent.removeChild(lowerAmount.parentElement.parentElement);
  327. this.removeItem(id);
  328. }
  329. }
  330. });
  331. }
  332. clearCart() {
  333. let cartItems = cart.map(item => item.id);
  334. cartItems.forEach(id => this.removeItem(id));
  335. while (cartContent.children.length > 0) {
  336. cartContent.removeChild(cartContent.children[0]);
  337. }
  338. }
  339. removeItem(id) {
  340. cart = cart.filter(item => item.id !== id);
  341. this.setCartValues(cart);
  342. Storage.saveCart(cart);
  343. let button = this.getSingleButton(id);
  344. button.disabled = false;
  345. button.innerHTML = "<i class='fas fa-shopping-cart'></i> Add to cart";
  346. }
  347. getSingleButton(id) {
  348. return buttonsDOM.find(button => button.dataset.id === id);
  349. }
  350. }
  351. /**
  352. * @description class cares about adding and removing product in and from the browser local storage
  353. * contains main four functions
  354. * 1- function {@link saveProducts} saves {@link products} into the browser local storage
  355. * 2- function {@link getProducts} reads {@link products} from the browser local storage
  356. * 3- function {@link saveCart} saves {@link cart} item into the browser local storage
  357. * 4- function {@link getCart} reads products from the browser local storage
  358. * @name Storage
  359. * @class
  360. * @example const strage = new Storage();
  361. * @summary BROWSER LOCAL STORAGE CREATING/READING OPERATIONS
  362. */
  363. class Storage {
  364. /**
  365. * @description saving products in the browser localt storage takes products array as a parameter
  366. * {@link products}
  367. * @method
  368. * @static
  369. * @inner
  370. * @name saveProducts
  371. * @this Storage
  372. * @param {Array.<Object>} pruducts to be saved
  373. * @example Storage.saveProducts(products);
  374. */
  375. static saveProducts(products) {
  376. localStorage.setItem("products", JSON.stringify(products));
  377. }
  378. /**
  379. * @description reading products from the browser localt storage takes the id of product as a parameter
  380. * and returns product matches that array
  381. * @method
  382. * @static
  383. * @inner
  384. * @this Storage
  385. * @name getProducts
  386. * @param {number} id of item to be fetched
  387. * @example Storage.getProducts(1);
  388. * @returns {Object}
  389. */
  390. static getProducts(id) {
  391. let products = JSON.parse(localStorage.getItem("products"));
  392. return products.find(product => product.id === id);
  393. }
  394. /**
  395. * @description saving cartItems array in the browser local storage takes the {@link cart} items array as a parameter
  396. * @method
  397. * @static
  398. * @inner
  399. * @this Storage
  400. * @name saveCart
  401. * @param {Array<Object>} cart items {@link cartItems} to be saved
  402. * @example Storage.saveCart(cart)
  403. */
  404. static saveCart(cart) {
  405. localStorage.setItem("cart", JSON.stringify(cart));
  406. }
  407. /**
  408. * @description reading (fetching) cartItems array {@link cart} from the browser local storage
  409. * @method
  410. * @static
  411. * @inner
  412. * @name getCart
  413. * @this Storage
  414. * @param {Array<Object>}
  415. * @returns {Array.<Object>} cart
  416. * @example Storage.getCart()
  417. */
  418. static getCart() {
  419. return localStorage.getItem("cart")
  420. ? JSON.parse(localStorage.getItem("cart"))
  421. : [];
  422. }
  423. }
  424. document.addEventListener("DOMContentLoaded", () => {
  425. const ui = new UI();
  426. const products = new Products();
  427. // set up methods
  428. ui.setupApp();
  429. // get all prooducts
  430. products
  431. .getProducts()
  432. .then(products => {
  433. ui.displayProducts(products);
  434. Storage.saveProducts(products);
  435. })
  436. .then(() => {
  437. ui.getBagButtons();
  438. ui.cartLogic();
  439. });
  440. });