From fa2bccb5189c3a6fa6e3c8afacbd4d2348b2c5fa Mon Sep 17 00:00:00 2001 From: Scott Kiener Date: Tue, 7 Nov 2023 15:34:31 -0500 Subject: [PATCH] CSR-1452 | More promo logic Watch lineItems to automatically run revalidate when the cart changes Add in auto add of addable vaps in beforeRouteEnter Create adding of addableVaps to order in promo helper --- src/helpers/promotions-helper.js | 26 ++++++++++++ .../add-vaps-modal-buttons.vue | 14 +++++-- src/layouts/payment-method/payment-method.vue | 40 ++++++++++++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/src/helpers/promotions-helper.js b/src/helpers/promotions-helper.js index a5d94789b..1689d9550 100644 --- a/src/helpers/promotions-helper.js +++ b/src/helpers/promotions-helper.js @@ -1,6 +1,7 @@ import store from "@/store"; import { storeActions } from "@/constants/store-actions"; import { partTypeStrings } from "@/constants/part-type-strings"; +import { deepClone } from "@/helpers/object-helper"; import baseMixin from "@/mixins/base-mixin.js"; export const promoPartNumberStrings = { @@ -126,6 +127,31 @@ export function getPromosThatMatchLineItemsOnOrder(promos, lineItemsOnOrder) { return matchingPromos; } +// Returns the vaps that the provided requires to be on the order to satisfy +// Items that are already in will not be returned +export function getVapsThatNeedToBeAddedToSatisfyPromos( + promos, + availableLineItems, + lineItemsOnOrder +) { + const clonedVaps = deepClone(lineItemsOnOrder.vaps ?? []); + const promosWithAddableVaps = getPromosWithAddableVaps(promos); + if (promosWithAddableVaps.length) { + const matchingLineItems = getLineItemsThatMatchPromos( + promosWithAddableVaps, + availableLineItems + ); + // Check if matching items are already in the order + const idsOfVapsAlreadyInOrder = clonedVaps.map((lineItem) => lineItem.id); + const vapsToAddToCart = matchingLineItems.filter( + (lineItem) => !idsOfVapsAlreadyInOrder.includes(lineItem.id) + ); + return vapsToAddToCart; + } else { + return []; + } +} + export function shouldStripPromoQueryString(fmgPageQueryValue) { return pagesToStripPromoQueryStringFrom.includes(fmgPageQueryValue); } diff --git a/src/layouts/payment-method/add-vaps-modal-buttons/add-vaps-modal-buttons.vue b/src/layouts/payment-method/add-vaps-modal-buttons/add-vaps-modal-buttons.vue index bb378c619..b4fa7a3b0 100644 --- a/src/layouts/payment-method/add-vaps-modal-buttons/add-vaps-modal-buttons.vue +++ b/src/layouts/payment-method/add-vaps-modal-buttons/add-vaps-modal-buttons.vue @@ -97,7 +97,11 @@ export default { this.$refs[modalName].closeModal(); }, addRainDefenseToCart(modalName, part) { - this.currentCartItems.vaps.push(part); + // Clone vaps array, then replace this.currentCartItems.vaps with it at the end + // in order to successfully trigger parent's watcher + const clonedVaps = this.currentCartItems.vaps.slice(0); + clonedVaps.push(part); + this.currentCartItems.vaps = clonedVaps; this.closeModal(modalName); const message = this.getRainDefenseAddedMessage(); this.$emit("added-to-cart", { @@ -133,19 +137,23 @@ export default { this.selectedWipers = ["REAR"]; } } + // Clone vaps array, then replace this.currentCartItems.vaps with it at the end + // in order to successfully trigger parent's watcher + const clonedVaps = this.currentCartItems.vaps.slice(0); itemsForCart.forEach((type) => { if (type === wipersOfferedStrings.FRONT) { part.frontWiperLineItems.forEach((part) => { - this.currentCartItems.vaps.push(part); + clonedVaps.push(part); }); } if (type === wipersOfferedStrings.REAR) { part.rearWiperLineItems.forEach((part) => { - this.currentCartItems.vaps.push(part); + clonedVaps.push(part); }); } }); + this.currentCartItems.vaps = clonedVaps; this.closeModal(modalName); diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 8a5f3a21d..6e87235b0 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -88,7 +88,8 @@ import { paymentMethods } from "@/constants/payment-method-constants"; import { experimentSettings } from "@/constants/experiments"; import { revalidatePromosAndValidateNewPromo, - getAddableVapsFromAvailableLineItems, + getPromosWithAddableVaps, + getVapsThatNeedToBeAddedToSatisfyPromos, } from "@/helpers/promotions-helper"; import { queryStrings } from "@/constants/query-strings"; import { getQuerystringParameter } from "@/helpers/querystring-helper"; @@ -177,7 +178,9 @@ export default { "payment-method" ); - availableLineItems.push(...(validatePromoResponse?.orderPromos ?? [])); + const newValidatedPromos = validatePromoResponse?.orderPromos ?? []; + + availableLineItems.push(...newValidatedPromos); // End of promo logic const taxedAvailableLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( @@ -201,6 +204,15 @@ export default { availableLineItems, lineItemsFromStore ); + // Add items that were not in the store yet but added via query string promo validation + lineItems.promos = lineItems.promos ?? []; + lineItems.promos.push(...newValidatedPromos); + const vapsToAddToCart = getVapsThatNeedToBeAddedToSatisfyPromos( + newValidatedPromos, + availableLineItems, + lineItems + ); + lineItems.vaps.push(...vapsToAddToCart); // Call the "next" function to complete the transition to this page. next((vm) => { @@ -412,11 +424,35 @@ export default { shouldDisplayPiaAlert() { return this.$route.params[this.routerParams.DISPLAY_PIA_ALERT]; }, + // Necessary to make the watcher of lineItems work + // JavaScript does not keep a record of the old value, only a reference to it's location in the memory. + // This creates a separate location for oldValue/newValue so they can be compared + // See Vue github issue #2164 + lineItemsCloneForWatcher() { + return Object.assign({}, this.lineItems); + }, }, watch: { customCtaCopy(newValue) { this.updateFooterButtonText(newValue); }, + lineItemsCloneForWatcher: { + handler(newValue, oldValue) { + if ( + !oldValue || + oldValue.length == 0 || + !oldValue.vaps || + !newValue || + newValue.length == 0 + ) { + return; + } + if (oldValue.vaps.length != newValue.vaps.length) { + this.revalidatePromos(); + } + }, + deep: true, + }, }, components: { funnelHeader,