diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index afb13c0e1..d00e08630 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -52,7 +52,15 @@ :text="recycleFeeCartItem.name" href="#!" @click-event="openModal(recyclingModalCmsWidgetName)" /> - {{ currencyFormatter.format(cartItem.subTotal) }} + {{ + `${ + cartItem.category == "promos" + ? "(" + currencyFormatter.format(cartItem.subTotal * -1) + ")" + : currencyFormatter.format(cartItem.subTotal) + }` + }} + @@ -88,6 +96,7 @@ import baseMixin from "@/mixins/base-mixin.js"; // Helpers import { deepClone } from "@/helpers/object-helper"; import { getHighestFullySatisfiedTier, getPackageContents } from "@/helpers/service-package-helper"; +import { getPromoCodeWithoutBundleIdentifier } from "@/helpers/promotions-helper"; // Constants import { partTypeStrings } from "@/constants/part-type-strings"; @@ -216,33 +225,16 @@ export default { cartItems.push(this.mobileFeeCartItem); } - // Create a cart item for each promo - this.promos.forEach((promoLineItem) => { - let cartItem = null; - - cartItem = { - name: promoLineItem?.promoCode, // get from CMS? - category: cartItemCategories.PROMOS, - cartItemType: promoLineItem?.partType, - isDisplayed: true, - subTotal: - (promoLineItem?.kitPrice ?? 0) + - (promoLineItem?.laborAmount ?? 0) + - (promoLineItem?.sellingPrice ?? 0), - salesTax: promoLineItem?.salesTax, - lineItems: [], - }; - - promoLineItem.cartItemType = cartItem.cartItemType; - cartItem.lineItems.push(promoLineItem); - - cartItems.push(cartItem); - }); - if (this.premiumAppointmentDiscountCartItem) { cartItems.push(this.premiumAppointmentDiscountCartItem); } + if (this.promoCartItems) { + this.promoCartItems.forEach((promoCartItem) => { + cartItems.push(promoCartItem); + }); + } + return cartItems; }, }, @@ -346,6 +338,7 @@ export default { category: cartItemCategories.VAPS, cartItemType: cartItemTypes.FRONT_WIPERS, isDisplayed: true, + isRemovable: true, subTotal: 0, salesTax: 0, lineItems: [], @@ -382,6 +375,7 @@ export default { category: cartItemCategories.VAPS, cartItemType: cartItemTypes.REAR_WIPERS, isDisplayed: true, + isRemovable: true, subTotal: 0, salesTax: 0, lineItems: [], @@ -547,10 +541,7 @@ export default { otherSupportingItemsCartItem() { let cartItem = null; - let otherSupportingItemsLineItems = baseMixin.methods.filterOutFees( - this.supportingItems - ); - otherSupportingItemsLineItems = otherSupportingItemsLineItems.filter( + const otherSupportingItemsLineItems = this.supportingItems.filter( (lineItem) => lineItem.partType != partTypeStrings.EARLY_BIRD ); @@ -597,6 +588,7 @@ export default { category: cartItemCategories.SUPPORTING_ITEMS, cartItemType: cartItemTypes.EARLY_BIRD, isDisplayed: true, + isRemovable: true, subTotal: 0, salesTax: 0, lineItems: [], @@ -615,6 +607,57 @@ export default { return cartItem; }, + promoCartItems() { + const promoCartItems = []; + + // clone the promos array + const promosClone = deepClone(this.promo); + // get unique promo codes + const uniquePromoCodes = [ + ...new Set( + this.promos.map((promo) => getPromoCodeWithoutBundleIdentifier(promo.promoCode)) + ), + ]; + + uniquePromoCodes.forEach((promoCode) => { + // get the codes with the prefix from the promo array + const promoLineItems = this.promos.filter( + (promo) => getPromoCodeWithoutBundleIdentifier(promo.promoCode) == promoCode + ); + + // Create a cart item for each promo + let cartItem = null; + + cartItem = { + name: promoCode, + category: cartItemCategories.PROMOS, + cartItemType: promoCode, + isDisplayed: true, + isRemovable: true, + subTotal: 0, + salesTax: 0, + lineItems: [], + }; + + promoLineItems.forEach((promoLineItem) => { + promoLineItem.cartItemType = cartItem.cartItemType; + + cartItem.subTotal += + (promoLineItem.kitPrice ?? 0) + + (promoLineItem.laborAmount ?? 0) + + (promoLineItem.sellingPrice ?? 0); + + cartItem.salesTax += promoLineItem.salesTax ?? 0; + + cartItem.lineItems.push(promoLineItem); + }); + + promoCartItems.push(cartItem); + }); + + return promoCartItems; + }, + removeLinkText() { return this.getCmsContent("RemoveCartItemTextWidget", "Text"); }, diff --git a/src/helpers/date-helper.js b/src/helpers/date-helper.js index 756863765..482440431 100644 --- a/src/helpers/date-helper.js +++ b/src/helpers/date-helper.js @@ -5,7 +5,7 @@ export function getDateDifferenceInDays(startDate, endDate) { // Use the built-in method to get the difference in milliseconds var difference = date1 - date2; // Convert milliseconds to days and return the result - return difference / (1000 * 3600 * 24); + return Math.floor(difference / (1000 * 3600 * 24)); } export function get12HourTimeFormat(time) { // Check correct time format and split into components diff --git a/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js b/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js new file mode 100644 index 000000000..3846a6bfd --- /dev/null +++ b/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js @@ -0,0 +1,90 @@ +import { mount, shallowMount } from "@vue/test-utils"; +import promoModalQuestion from "./promo-modal-question"; + +jest.mock("@/digital-components/textbox-question/textbox-question", () => ({ + getCmsContent: jest.fn((widgetName, cmsFieldName) => { + return widgetName[cmsFieldName]; + }), +})); + +jest.mock("@/digital-components/modal/modal", () => ({ + methods: { + closeModal: jest.fn(), + resetButtonStyle: jest.fn(), + }, +})); + +const linkWidgetName = "linkWidgetName"; +const modalWidgetName = "modalWidgetName"; + +const mockLinkCmsContent = { + BodyText: "Sample link body text here.", +}; + +const mockModalCmsContent = { + FooterText: "Sample modal footer text here.", +}; + +const mockMixin = { + methods: { + getCmsContent: jest.fn((widgetName, cmsFieldName) => { + if (widgetName === linkWidgetName) { + return mockLinkCmsContent[cmsFieldName]; + } + + if (widgetName === modalWidgetName) { + return mockModalCmsContent[cmsFieldName]; + } + + return null; + }), + }, +}; + +describe("promo-modal-question.vue", () => { + it("Should reset all alerts on onModalClosed", async () => { + // Arrange + + const wrapper = mount(promoModalQuestion, { + mixins: [mockMixin], + props: { + linkWidgetName: linkWidgetName, + modalWidgetName: modalWidgetName, + }, + attachTo: document.body, + }); + + // Act + wrapper.vm.onModalClosed(); + + // Assert + expect(wrapper.vm.displayInvalidPromoAlert).toBe(false); + expect(wrapper.vm.displayInvalidOnOrderPromoAlert).toBe(false); + }); + it("Should emit update:modelValue on Modal closed", async () => { + // Arrange + const lineItems = { + glassParts: [], + supportingItems: [], + vaps: [], + promos: [], + }; + + const wrapper = mount(promoModalQuestion, { + mixins: [mockMixin], + props: { + modelValue: lineItems, + linkWidgetName: linkWidgetName, + modalWidgetName: modalWidgetName, + }, + attachTo: document.body, + }); + + // Act + + await wrapper.vm.onModalClosed(); + + // Assert + expect(wrapper.emitted("update:modelValue")).toEqual([[lineItems]]); + }); +}); diff --git a/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue b/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue index db04dfb00..ac42fce2e 100644 --- a/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue +++ b/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue @@ -98,16 +98,16 @@ export default { return this.getCmsContent("AlertInvalidPromoOnOrderWidget", "HeadlineText"); }, InvalidPromoOnOrderText() { - return this.PromoText?.replaceAll("{custom:NEWPROMOCODE}", this.promoCode).replaceAll( - "{custom:OLDPROMOCODE}", - this.promoCode - ); + return this.PromoOnOrderText?.replaceAll( + "{custom:NEWPROMOCODE}", + this.promoCode.toUpperCase() + ).replaceAll("{custom:OLDPROMOCODE}", this.getPromoCode().toUpperCase()); }, PromoText() { return this.getCmsContent("AlertInvalidPromoWidget", "HeadlineText"); }, InvalidPromoText() { - return this.PromoText?.replaceAll("{custom:PROMOCODE}", this.promoCode); + return this.PromoText?.replaceAll("{custom:PROMOCODE}", this.promoCode.toUpperCase()); }, removeLinkText() { return this.getCmsContent("RemoveCartItemTextWidget", "Text"); @@ -115,6 +115,7 @@ export default { }, methods: { resetAlerts() { + this.displayInvalidOnOrderPromoAlert = false; this.displayInvalidPromoAlert = false; }, resetsOnPromoInput() { @@ -123,6 +124,9 @@ export default { getPromoList() { return this.lineItems.promos; }, + getPromoCode() { + return this.lineItems.promos?.[0].promoCode; + }, openModal() { this.modal.openModal(); }, @@ -130,7 +134,9 @@ export default { this.modal.closeModal(); }, onModalClosed() { + this.resetAlerts(); this.$emit("update:modelValue", this.lineItems); + this.promoCode = ""; }, focusOnPromoInput() { const input = document.getElementById(this.promoTextInputId); @@ -139,16 +145,8 @@ export default { resetModalButtonStyle() { this.modal.resetButtonStyle(); }, - getPromoAddedMessage() { - return this.AddPromoSuccessMessageFromCms?.replaceAll( - "{custom:PROMOCODE}", - this.promoCode - ); - }, async getPromoCodeData(promoCode, lineItems, availableVaps, pageNameToLog = null) { const pageName = pageNameToLog ?? this.$options?.name; - //const addableVaps = getAddableVapsFromAvailableLineItems(availableLineItems); - //const lineItemsToUse = lineItems.Length > 0 ? lineItems : null; const promoValidationResponse = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA, { @@ -216,16 +214,16 @@ export default { this.lineItems?.promos.push(...promoCodeData.promoCode); this.lineItems.vaps?.push(...getVaps); this.closeModal(); - this.promoCode = ""; } else { if (promoCodeData.errorCode == promoErrorCodes.PROMO_STACKING_NOT_ALLOWED) { - this.displayInvalidPromoOnOrderAlert = true; + this.displayInvalidOnOrderPromoAlert = true; + this.focusOnPromoInput(); + this.resetModalButtonStyle(); + } else { + this.displayInvalidPromoAlert = true; this.focusOnPromoInput(); this.resetModalButtonStyle(); } - this.displayInvalidPromoAlert = true; - this.focusOnPromoInput(); - this.resetModalButtonStyle(); } } }, @@ -253,7 +251,7 @@ export default {