Merge remote-tracking branch 'origin/develop' into feature/skiener/CSR-1452
This commit is contained in:
commit
4556e28457
4 changed files with 179 additions and 48 deletions
|
|
@ -52,7 +52,15 @@
|
||||||
:text="recycleFeeCartItem.name"
|
:text="recycleFeeCartItem.name"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="openModal(recyclingModalCmsWidgetName)" />
|
@click-event="openModal(recyclingModalCmsWidgetName)" />
|
||||||
<span>{{ currencyFormatter.format(cartItem.subTotal) }}</span>
|
<span
|
||||||
|
>{{
|
||||||
|
`${
|
||||||
|
cartItem.category == "promos"
|
||||||
|
? "(" + currencyFormatter.format(cartItem.subTotal * -1) + ")"
|
||||||
|
: currencyFormatter.format(cartItem.subTotal)
|
||||||
|
}`
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Sub total, sales tax, total columns -->
|
<!-- Sub total, sales tax, total columns -->
|
||||||
|
|
@ -88,6 +96,7 @@ import baseMixin from "@/mixins/base-mixin.js";
|
||||||
// Helpers
|
// Helpers
|
||||||
import { deepClone } from "@/helpers/object-helper";
|
import { deepClone } from "@/helpers/object-helper";
|
||||||
import { getHighestFullySatisfiedTier, getPackageContents } from "@/helpers/service-package-helper";
|
import { getHighestFullySatisfiedTier, getPackageContents } from "@/helpers/service-package-helper";
|
||||||
|
import { getPromoCodeWithoutBundleIdentifier } from "@/helpers/promotions-helper";
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
|
|
@ -216,33 +225,16 @@ export default {
|
||||||
cartItems.push(this.mobileFeeCartItem);
|
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) {
|
if (this.premiumAppointmentDiscountCartItem) {
|
||||||
cartItems.push(this.premiumAppointmentDiscountCartItem);
|
cartItems.push(this.premiumAppointmentDiscountCartItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.promoCartItems) {
|
||||||
|
this.promoCartItems.forEach((promoCartItem) => {
|
||||||
|
cartItems.push(promoCartItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return cartItems;
|
return cartItems;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -346,6 +338,7 @@ export default {
|
||||||
category: cartItemCategories.VAPS,
|
category: cartItemCategories.VAPS,
|
||||||
cartItemType: cartItemTypes.FRONT_WIPERS,
|
cartItemType: cartItemTypes.FRONT_WIPERS,
|
||||||
isDisplayed: true,
|
isDisplayed: true,
|
||||||
|
isRemovable: true,
|
||||||
subTotal: 0,
|
subTotal: 0,
|
||||||
salesTax: 0,
|
salesTax: 0,
|
||||||
lineItems: [],
|
lineItems: [],
|
||||||
|
|
@ -382,6 +375,7 @@ export default {
|
||||||
category: cartItemCategories.VAPS,
|
category: cartItemCategories.VAPS,
|
||||||
cartItemType: cartItemTypes.REAR_WIPERS,
|
cartItemType: cartItemTypes.REAR_WIPERS,
|
||||||
isDisplayed: true,
|
isDisplayed: true,
|
||||||
|
isRemovable: true,
|
||||||
subTotal: 0,
|
subTotal: 0,
|
||||||
salesTax: 0,
|
salesTax: 0,
|
||||||
lineItems: [],
|
lineItems: [],
|
||||||
|
|
@ -547,10 +541,7 @@ export default {
|
||||||
otherSupportingItemsCartItem() {
|
otherSupportingItemsCartItem() {
|
||||||
let cartItem = null;
|
let cartItem = null;
|
||||||
|
|
||||||
let otherSupportingItemsLineItems = baseMixin.methods.filterOutFees(
|
const otherSupportingItemsLineItems = this.supportingItems.filter(
|
||||||
this.supportingItems
|
|
||||||
);
|
|
||||||
otherSupportingItemsLineItems = otherSupportingItemsLineItems.filter(
|
|
||||||
(lineItem) => lineItem.partType != partTypeStrings.EARLY_BIRD
|
(lineItem) => lineItem.partType != partTypeStrings.EARLY_BIRD
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -597,6 +588,7 @@ export default {
|
||||||
category: cartItemCategories.SUPPORTING_ITEMS,
|
category: cartItemCategories.SUPPORTING_ITEMS,
|
||||||
cartItemType: cartItemTypes.EARLY_BIRD,
|
cartItemType: cartItemTypes.EARLY_BIRD,
|
||||||
isDisplayed: true,
|
isDisplayed: true,
|
||||||
|
isRemovable: true,
|
||||||
subTotal: 0,
|
subTotal: 0,
|
||||||
salesTax: 0,
|
salesTax: 0,
|
||||||
lineItems: [],
|
lineItems: [],
|
||||||
|
|
@ -615,6 +607,57 @@ export default {
|
||||||
|
|
||||||
return cartItem;
|
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() {
|
removeLinkText() {
|
||||||
return this.getCmsContent("RemoveCartItemTextWidget", "Text");
|
return this.getCmsContent("RemoveCartItemTextWidget", "Text");
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ export function getDateDifferenceInDays(startDate, endDate) {
|
||||||
// Use the built-in method to get the difference in milliseconds
|
// Use the built-in method to get the difference in milliseconds
|
||||||
var difference = date1 - date2;
|
var difference = date1 - date2;
|
||||||
// Convert milliseconds to days and return the result
|
// Convert milliseconds to days and return the result
|
||||||
return difference / (1000 * 3600 * 24);
|
return Math.floor(difference / (1000 * 3600 * 24));
|
||||||
}
|
}
|
||||||
export function get12HourTimeFormat(time) {
|
export function get12HourTimeFormat(time) {
|
||||||
// Check correct time format and split into components
|
// Check correct time format and split into components
|
||||||
|
|
|
||||||
|
|
@ -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]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -98,16 +98,16 @@ export default {
|
||||||
return this.getCmsContent("AlertInvalidPromoOnOrderWidget", "HeadlineText");
|
return this.getCmsContent("AlertInvalidPromoOnOrderWidget", "HeadlineText");
|
||||||
},
|
},
|
||||||
InvalidPromoOnOrderText() {
|
InvalidPromoOnOrderText() {
|
||||||
return this.PromoText?.replaceAll("{custom:NEWPROMOCODE}", this.promoCode).replaceAll(
|
return this.PromoOnOrderText?.replaceAll(
|
||||||
"{custom:OLDPROMOCODE}",
|
"{custom:NEWPROMOCODE}",
|
||||||
this.promoCode
|
this.promoCode.toUpperCase()
|
||||||
);
|
).replaceAll("{custom:OLDPROMOCODE}", this.getPromoCode().toUpperCase());
|
||||||
},
|
},
|
||||||
PromoText() {
|
PromoText() {
|
||||||
return this.getCmsContent("AlertInvalidPromoWidget", "HeadlineText");
|
return this.getCmsContent("AlertInvalidPromoWidget", "HeadlineText");
|
||||||
},
|
},
|
||||||
InvalidPromoText() {
|
InvalidPromoText() {
|
||||||
return this.PromoText?.replaceAll("{custom:PROMOCODE}", this.promoCode);
|
return this.PromoText?.replaceAll("{custom:PROMOCODE}", this.promoCode.toUpperCase());
|
||||||
},
|
},
|
||||||
removeLinkText() {
|
removeLinkText() {
|
||||||
return this.getCmsContent("RemoveCartItemTextWidget", "Text");
|
return this.getCmsContent("RemoveCartItemTextWidget", "Text");
|
||||||
|
|
@ -115,6 +115,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetAlerts() {
|
resetAlerts() {
|
||||||
|
this.displayInvalidOnOrderPromoAlert = false;
|
||||||
this.displayInvalidPromoAlert = false;
|
this.displayInvalidPromoAlert = false;
|
||||||
},
|
},
|
||||||
resetsOnPromoInput() {
|
resetsOnPromoInput() {
|
||||||
|
|
@ -123,6 +124,9 @@ export default {
|
||||||
getPromoList() {
|
getPromoList() {
|
||||||
return this.lineItems.promos;
|
return this.lineItems.promos;
|
||||||
},
|
},
|
||||||
|
getPromoCode() {
|
||||||
|
return this.lineItems.promos?.[0].promoCode;
|
||||||
|
},
|
||||||
openModal() {
|
openModal() {
|
||||||
this.modal.openModal();
|
this.modal.openModal();
|
||||||
},
|
},
|
||||||
|
|
@ -130,7 +134,9 @@ export default {
|
||||||
this.modal.closeModal();
|
this.modal.closeModal();
|
||||||
},
|
},
|
||||||
onModalClosed() {
|
onModalClosed() {
|
||||||
|
this.resetAlerts();
|
||||||
this.$emit("update:modelValue", this.lineItems);
|
this.$emit("update:modelValue", this.lineItems);
|
||||||
|
this.promoCode = "";
|
||||||
},
|
},
|
||||||
focusOnPromoInput() {
|
focusOnPromoInput() {
|
||||||
const input = document.getElementById(this.promoTextInputId);
|
const input = document.getElementById(this.promoTextInputId);
|
||||||
|
|
@ -139,16 +145,8 @@ export default {
|
||||||
resetModalButtonStyle() {
|
resetModalButtonStyle() {
|
||||||
this.modal.resetButtonStyle();
|
this.modal.resetButtonStyle();
|
||||||
},
|
},
|
||||||
getPromoAddedMessage() {
|
|
||||||
return this.AddPromoSuccessMessageFromCms?.replaceAll(
|
|
||||||
"{custom:PROMOCODE}",
|
|
||||||
this.promoCode
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async getPromoCodeData(promoCode, lineItems, availableVaps, pageNameToLog = null) {
|
async getPromoCodeData(promoCode, lineItems, availableVaps, pageNameToLog = null) {
|
||||||
const pageName = pageNameToLog ?? this.$options?.name;
|
const pageName = pageNameToLog ?? this.$options?.name;
|
||||||
//const addableVaps = getAddableVapsFromAvailableLineItems(availableLineItems);
|
|
||||||
//const lineItemsToUse = lineItems.Length > 0 ? lineItems : null;
|
|
||||||
const promoValidationResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
const promoValidationResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA,
|
storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA,
|
||||||
{
|
{
|
||||||
|
|
@ -216,16 +214,16 @@ export default {
|
||||||
this.lineItems?.promos.push(...promoCodeData.promoCode);
|
this.lineItems?.promos.push(...promoCodeData.promoCode);
|
||||||
this.lineItems.vaps?.push(...getVaps);
|
this.lineItems.vaps?.push(...getVaps);
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
this.promoCode = "";
|
|
||||||
} else {
|
} else {
|
||||||
if (promoCodeData.errorCode == promoErrorCodes.PROMO_STACKING_NOT_ALLOWED) {
|
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.focusOnPromoInput();
|
||||||
this.resetModalButtonStyle();
|
this.resetModalButtonStyle();
|
||||||
}
|
}
|
||||||
this.displayInvalidPromoAlert = true;
|
|
||||||
this.focusOnPromoInput();
|
|
||||||
this.resetModalButtonStyle();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -253,7 +251,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.applied-promo {
|
.applied-promo {
|
||||||
padding-right: 5rem;
|
padding-right: 4rem;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue