From 6779ab0351518501d9485aaa6367e9eaf3a41cee Mon Sep 17 00:00:00 2001 From: Jevy Larano <32891190+jrlarano@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:10:14 +0800 Subject: [PATCH] Jl/multipack (#258) * Calculate orig price and savings * Display multipack individual price and calculated savings * Add price calculation to offer modal * Update clear translation --- .../components/common/offer-overview.ts | 67 ++++++++- .../components/common/shopping-list.ts | 135 +++++++++++++----- .../core-ui/components/helpers/component.ts | 38 +++++ locales/sv_SE.ts | 2 +- 4 files changed, 205 insertions(+), 37 deletions(-) diff --git a/lib/kits/core-ui/components/common/offer-overview.ts b/lib/kits/core-ui/components/common/offer-overview.ts index 82146eb1..f35f8807 100644 --- a/lib/kits/core-ui/components/common/offer-overview.ts +++ b/lib/kits/core-ui/components/common/offer-overview.ts @@ -10,7 +10,9 @@ import { parseDateStr, updateShoppingList, displayOfferMessage, - getLocaleCode + getLocaleCode, + calculateProductPrice, + getTotalQuantityByOffer } from '../helpers/component'; import {request, V2Offer} from '../../../core'; import * as clientLocalStorage from '../../../../storage/client-local'; @@ -121,6 +123,7 @@ const OfferOverview = ({ }) => { template = template?.innerHTML || defaultTemplate; let container: HTMLDivElement | null = null; + let transformedOffer; const translations = { localeCode: scriptEls.localeCode @@ -135,7 +138,7 @@ const OfferOverview = ({ const render = async () => { try { - const transformedOffer = + transformedOffer = type === 'paged' ? await fetchOffer(offer.id) : await transformIncitoOffer(offer); @@ -188,10 +191,14 @@ const OfferOverview = ({ (offer) => offer.id === product.id ); - const price = useOfferPrice + let price = useOfferPrice ? offer.price || offer.pricing.price : product?.price; + if (offer.piece_count?.from > 1) { + price = (price + (offer.savings || 0)) / offer.piece_count.from; + } + const priceCurrency = offer.currency_code || offer.pricing?.currency || currency; @@ -493,6 +500,59 @@ const OfferOverview = ({ }); }; + const addLocalStorageListener = () => { + window.addEventListener('tjek_shopping_list_update', () => { + const {localeCode, currency} = translations; + const productEls = container?.querySelectorAll( + '.sgn-product-details' + ); + let priceCurrency = currency; + const storedPublicationOffers = clientLocalStorage.get( + 'publication-saved-offers' + ); + + productEls?.forEach((productEl: HTMLElement) => { + const priceEl = + productEl.querySelector('.sgn-product-price'); + const productId = productEl.dataset.offerProductId; + const product = storedPublicationOffers.find( + (product) => product.id === productId + ); + priceCurrency = product?.pricing?.currency || currency; + + if (priceEl && !product) { + const offerProduct = transformedOffer.products.find( + (product) => product.id === productId + ); + + priceEl.innerHTML = formatPrice( + offerProduct.price, + localeCode, + priceCurrency + ); + return; + } + + if (priceEl && product?.pricing?.price) { + const totalQuantityByOffer = getTotalQuantityByOffer( + storedPublicationOffers, + product.offerId + ); + const priceNum = calculateProductPrice( + product, + totalQuantityByOffer + ); + + priceEl.innerHTML = formatPrice( + priceNum, + localeCode, + priceCurrency + ); + } + }); + }); + }; + const addEventListeners = () => { document.querySelector('.sgn-modal-container')?.focus(); @@ -500,6 +560,7 @@ const OfferOverview = ({ addShoppingListListener(); addProductListener(); closeModalListener(); + addLocalStorageListener(); }; return {render}; diff --git a/lib/kits/core-ui/components/common/shopping-list.ts b/lib/kits/core-ui/components/common/shopping-list.ts index 3ee8367f..a32361f9 100644 --- a/lib/kits/core-ui/components/common/shopping-list.ts +++ b/lib/kits/core-ui/components/common/shopping-list.ts @@ -8,7 +8,9 @@ import { formatPrice, translate, updateShoppingList, - getLocaleCode + getLocaleCode, + calculateProductPrice, + getTotalQuantityByOffer } from '../helpers/component'; import './shopping-list.styl'; @@ -185,17 +187,29 @@ const ShoppingList = ({ const transformSavedOffers = (savedOffers) => { const {localeCode, currency} = translations; - return (savedOffers || []).map((offer, index) => ({ - index, - ...offer, - price: offer?.pricing?.price - ? formatPrice( - offer?.pricing?.price * (offer?.quantity || 1), - localeCode, - offer?.pricing?.currency || currency - ) - : null - })); + return (savedOffers || []).map((offer, index) => { + const totalQuantityByOffer = getTotalQuantityByOffer( + savedOffers, + offer.offerId + ); + const productPrice = calculateProductPrice( + offer, + totalQuantityByOffer + ); + + return { + index, + ...offer, + totalQuantityByOffer, + price: productPrice + ? formatPrice( + productPrice, + localeCode, + offer?.pricing?.currency || currency + ) + : null + }; + }); }; const addTickerListener = () => { @@ -350,10 +364,20 @@ const ShoppingList = ({ ); const priceCurrency = storedPublicationOffers?.[0]?.pricing?.currency || currency; + let totalPrice = 0; + + storedPublicationOffers?.forEach((product) => { + const totalQuantityByOffer = getTotalQuantityByOffer( + storedPublicationOffers, + product.offerId + ); + const priceNum = calculateProductPrice( + product, + totalQuantityByOffer + ); - const totalPrice = storedPublicationOffers?.reduce((acc, product) => { - return acc + product.pricing.price * product.quantity; - }, 0); + totalPrice += priceNum; + }); return totalPrice ? formatPrice(totalPrice, localeCode, priceCurrency) @@ -389,8 +413,6 @@ const ShoppingList = ({ (product) => product.id === productId ); - const priceCurrency = product?.pricing?.currency || currency; - if (quantityTxt) { quantityTxt.value = action === 'plus' ? `${++quantity}` : `${--quantity}`; @@ -399,16 +421,6 @@ const ShoppingList = ({ productEl.remove(); } - if (priceEl && product?.pricing?.price && quantity) { - const priceNum = product?.pricing?.price * (quantity || 1); - - priceEl.innerHTML = formatPrice( - priceNum, - localeCode, - priceCurrency - ); - } - updateShoppingList( { ...product, @@ -419,12 +431,6 @@ const ShoppingList = ({ }, action ); - - if (totalPriceEL && getTotalPrice()) { - totalPriceEL.innerHTML = getTotalPrice(); - } else { - totalPriceContainer?.remove(); - } } }; @@ -450,6 +456,68 @@ const ShoppingList = ({ }); }; + const addLocalStorageListener = () => { + window.addEventListener('tjek_shopping_list_update', () => { + const {localeCode, currency} = translations; + const productEls = document.querySelectorAll( + '.sgn-shopping-list-item-container' + ); + let priceCurrency = currency; + let totalPrice = 0; + + const totalPriceEL = container?.querySelector( + '.sgn-shopping-list-content-price-total' + ); + const totalPriceContainer = + container?.querySelector( + '.sgn-shopping-list-content-container-total' + ); + const storedPublicationOffers = clientLocalStorage.get( + 'publication-saved-offers' + ); + + productEls.forEach((productEl: HTMLElement) => { + const priceEl = productEl.querySelector( + '.sgn-shopping-list-content-price' + ); + const productId = productEl.dataset.offerProductId; + const product = storedPublicationOffers.find( + (product) => product.id === productId + ); + priceCurrency = product?.pricing?.currency || currency; + + if (priceEl && product?.pricing?.price) { + const totalQuantityByOffer = getTotalQuantityByOffer( + storedPublicationOffers, + product.offerId + ); + const priceNum = calculateProductPrice( + product, + totalQuantityByOffer + ); + + totalPrice += priceNum; + + priceEl.innerHTML = formatPrice( + priceNum, + localeCode, + priceCurrency + ); + } + }); + + if (totalPriceEL && totalPrice) { + totalPriceEL.innerHTML = formatPrice( + totalPrice, + localeCode, + priceCurrency + ); + } else { + totalPriceContainer?.remove(); + } + }); + }; + const formatListToShare = (data, newLineDelimiter = `\n`) => { let offerStr = ''; @@ -474,6 +542,7 @@ const ShoppingList = ({ addPrintListener(); addShareListener(); addQuantityListener(); + addLocalStorageListener(); }; return {render}; diff --git a/lib/kits/core-ui/components/helpers/component.ts b/lib/kits/core-ui/components/helpers/component.ts index ab8a26ba..c1424ad6 100644 --- a/lib/kits/core-ui/components/helpers/component.ts +++ b/lib/kits/core-ui/components/helpers/component.ts @@ -322,8 +322,11 @@ export const updateShoppingList = (offer, action: 'plus' | 'minus') => { const currency = offer.currency_code || offer.pricing.currency; let shopListOffer = { + offerId: offer.id, id: offer.id, name: offer.name, + pieceCount: offer.piece_count, + savings: offer.savings, pricing: { price: offer.price, currency @@ -345,8 +348,11 @@ export const updateShoppingList = (offer, action: 'plus' | 'minus') => { ); if (product) { shopListOffer = { + offerId: offer.id, id: product.id, name: product.title, + pieceCount: offer.piece_count, + savings: offer.savings, pricing: { price: useOfferPrice ? offer.price || offer.pricing.price @@ -453,3 +459,35 @@ export const getLocaleCode = (countryId: string): string => { return countryLocaleMap[countryId] || ''; }; + +export const calculateProductPrice = (offer, totalQuantityByOffer = 1) => { + let productPrice = 0; + const offerPrice = offer.pricing.price; // Individual price per piece + + for (let i = offer?.quantity || 1; i >= 1; i--) { + if (offer.pieceCount?.from > 1) { + if (i % offer.pieceCount?.from === 0) { + productPrice += offerPrice; + i -= offer.pieceCount.from - 1; + } else if (totalQuantityByOffer % offer.pieceCount?.from === 0) { + productPrice += offerPrice / offer.pieceCount.from; + } else { + productPrice += + (offerPrice + offer.savings) / offer.pieceCount.from; + } + } else { + productPrice += offerPrice; + } + } + + return productPrice; +}; + +export const getTotalQuantityByOffer = (savedOffers, offerId) => { + return (savedOffers || []).reduce((totalQuantity, offer) => { + if (offer.offerId === offerId) { + totalQuantity += offer.quantity || 1; + } + return totalQuantity; + }, 0); +}; diff --git a/locales/sv_SE.ts b/locales/sv_SE.ts index b7e8e478..80e508dd 100644 --- a/locales/sv_SE.ts +++ b/locales/sv_SE.ts @@ -1,7 +1,7 @@ export default { locale_code: 'sv_SE', publication_viewer_shopping_list_label: 'Inköpslista', - publication_viewer_shopping_list_clear_button: 'Tydlig lista', + publication_viewer_shopping_list_clear_button: 'Rensa lista', publication_viewer_delete_crossed_out_button: 'Ta bort överstrukna objekt', publication_viewer_print_button: 'Skriva ut', publication_viewer_download_button: 'Ladda ner',