diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index ed880269c..16e0803e6 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -84,6 +84,10 @@ const endpoints = { url: "/parts/api/v1/parts/supporting-items", method: "POST", }, + GetRecalPart: { + url: "/parts/api/v1/parts/recal-parts", + method: "GET", + }, GetAlertReasons: { url: "/location/api/v1/location/alert-reasons", method: "GET", diff --git a/src/constants/experiments.js b/src/constants/experiments.js index eeabeb7d6..83555fc04 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -12,6 +12,7 @@ const experimentSettings = { SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA", IS_EMAIL_OPTIONAL: "isEmailOptional", SERVICE_PACKAGE_DISCOUNT: "OfferServicePackageDiscount", + PROMO_ON_PACKAGE: "Offer_Promo_On_Pkg", RECAL_PRICE_REMOVE: "RecalPriceRemove", INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL: "NextGen_InternalInsuranceTabDisplayThreshold", INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL: "NextGen_ExternalInsuranceTabDisplayThreshold", diff --git a/src/constants/external-parameters.js b/src/constants/external-parameters.js new file mode 100644 index 000000000..b0adaf836 --- /dev/null +++ b/src/constants/external-parameters.js @@ -0,0 +1,6 @@ +const externalParameterStatus = { + NOT_SET: null, + ACTIVE: 1, + INACTIVE: 0, +}; +export { externalParameterStatus }; diff --git a/src/constants/insurance.js b/src/constants/insurance.js index 1df261d63..f7550513b 100644 --- a/src/constants/insurance.js +++ b/src/constants/insurance.js @@ -29,6 +29,19 @@ export function coverageStatusValue(intCoverageStatus) { } } +export function coverageStatusEnum(strCoverageStatus) { + switch (strCoverageStatus) { + case coverageStatus.PENDING: + return 0; + case coverageStatus.NOCOMP: + return 1; + case coverageStatus.VERIFIED: + return 2; + default: + return null; + } +} + export const coverageType = { NONE: "None", DEDUCTIBLE: "Deductible", @@ -50,3 +63,18 @@ export function coverageTypeValue(intCoverageType) { return null; } } + +export function coverageTypeEnum(strCoverageType) { + switch (strCoverageType) { + case coverageType.NONE: + return 0; + case coverageType.DEDUCTIBLE: + return 1; + case coverageType.ITAC: + return 2; + case coverageType.NOCOMP: + return 3; + default: + return null; + } +} diff --git a/src/constants/part-type-strings.js b/src/constants/part-type-strings.js index 3ad66c40a..4681e2776 100644 --- a/src/constants/part-type-strings.js +++ b/src/constants/part-type-strings.js @@ -2,6 +2,7 @@ const partTypeStrings = { FRONT_WIPER: "FRONT WIPER", REAR_WIPER: "REAR WIPER", RAIN_DEFENSE: "RAIN DEFENSE", + ADAS_RECALIBRATION: "ADAS RECALIBRATION", RECALIBRATION: "RECALIBRATION", REPLACE_FEE: "REPLACE FEE", RECYCLE_FEE: "RECYCLE FEE", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 4cd03771e..c9592c315 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -54,8 +54,10 @@ const storeActions = { REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA: "revalidateOrderPromosAndSaveServerData", GET_INSURANCE_COMPANY_LIST: "getInsuranceCompanyList", GET_BILL_TO_ACCOUNT_NUMBER: "getBillToAccountNumber", + GET_RECAL_PARTS_AND_SAVE_TO_LINE_ITEMS: "getRecalPartsAndSaveToLineItems", // DEPENDENCY MUTATIONS + RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleStateAndDependencies", RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies", RESET_REGISTRATION_STATE_AND_DEPENDENCIES: "resetRegistrationAndDependencies", RESET_PARTS_STATE_AND_DEPENDENCIES: "resetPartsAndDependencies", diff --git a/src/fmg-components/address-questions/address-questions.spec.js b/src/fmg-components/address-questions/address-questions.spec.js index 5267bb89c..6be242d14 100644 --- a/src/fmg-components/address-questions/address-questions.spec.js +++ b/src/fmg-components/address-questions/address-questions.spec.js @@ -602,6 +602,24 @@ function setupMocks({ if (props) resultingMountOptions.propsData = props; + if (isShallowMount) { + resultingMountOptions.global.stubs = { + ...(resultingMountOptions.global.stubs ?? {}), + textboxQuestion: { + template: ``, + methods: { + handleChange: jest.fn(), + }, + }, + dropdownQuestion: { + template: ``, + methods: { + handleChange: jest.fn(), + }, + }, + }; + } + const wrapper = isShallowMount ? shallowMount(addressQuestions, resultingMountOptions) : mount(addressQuestions, resultingMountOptions); diff --git a/src/fmg-components/address-questions/address-questions.vue b/src/fmg-components/address-questions/address-questions.vue index 7acd0f4f0..4226b014c 100644 --- a/src/fmg-components/address-questions/address-questions.vue +++ b/src/fmg-components/address-questions/address-questions.vue @@ -371,6 +371,12 @@ export default { self.addressModel.state = state; self.addressModel.zipCode = zipCode; + this.$refs.autocomplete.handleChange(streetNumber); + this.$refs.apartmentNumberOrBusinessName.handleChange(route); + this.$refs.city.handleChange(city); + this.$refs.state.handleChange(state); + this.$refs.zipCode.handleChange(zipCode); + if (!this.displayVerificationWarning) { this.displayVerificationWarning = place.partial_match || !isAddressComplete; } diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 90c1adf98..67bc9f961 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -110,7 +110,7 @@ {{ subtotalText }} {{ getLineItemAmount(subTotal, showCoverageAsPending) }} -
+
{{ salesTaxText }} {{ getLineItemAmount(salesTax) }}
@@ -188,6 +188,8 @@ export default { insuranceCompanyName: String, showInsuranceCoverageAs: String, shouldHideRecalibration: Boolean, + isItac: Boolean, + isNoComp: Boolean, }, data() { return { @@ -305,8 +307,8 @@ export default { if (!this.lineItems || this.lineItems.length < 1) return; const lineItemsCopy = deepClone(this.lineItems); - lineItemsCopy.supportingItems = lineItemsCopy.supportingItems - ? baseMixin.methods.filterOutRecalibration(lineItemsCopy?.supportingItems) + lineItemsCopy.glassParts = lineItemsCopy.glassParts + ? baseMixin.methods.filterOutRecalibration(lineItemsCopy?.glassParts) : []; return lineItemsCopy; }, @@ -316,6 +318,10 @@ export default { showCoverageAsVerified() { return this.showInsuranceCoverageAs === coverageStatus.VERIFIED; }, + showSalesTax() { + const tax = parseFloat(this.salesTax).toFixed(2); + return tax > 0; + }, currentDeductible() { const deductible = this.insuranceDeductible; if (!(deductible === 0 || deductible > 0)) { @@ -1007,6 +1013,16 @@ export default { }, salesTax() { if (!this.lineItems || this.lineItems.length < 1) return; + + if ( + this.isInsurance && + !this.showCoverageAsVerified && + !this.isItac && + !this.isNoComp + ) { + return 0; + } + return this.isInsurance || !this.shouldHideRecalibration ? baseMixin.methods.getSalesTax(this.lineItems) // calculate with recal (if on order) : baseMixin.methods.getSalesTax(this.lineItemsWithoutRecal); // calculated without recal diff --git a/src/fmg-components/funnel-sub-header/funnel-sub-header.vue b/src/fmg-components/funnel-sub-header/funnel-sub-header.vue index fa8aea13f..54c8aa34d 100644 --- a/src/fmg-components/funnel-sub-header/funnel-sub-header.vue +++ b/src/fmg-components/funnel-sub-header/funnel-sub-header.vue @@ -53,7 +53,7 @@ export default { return this.leftAlignHeader ? "justify-content-start" : "justify-content-center"; }, subText() { - return this.getCmsContent(this.cmsWidgetName, "HeaderSubText"); + return this.getCmsContent(this.cmsWidgetName, "SubheaderText"); }, backButtonAccessibleText() { return this.getCmsContent(this.cmsWidgetName, "BackButtonAccessibleText"); diff --git a/src/global-methods.js b/src/global-methods.js index 6afeef53d..22512fe41 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -6,6 +6,7 @@ import store from "@/store"; import { applicationConfig } from "@/constants/application-config.js"; import { GaCategories, GaActions, GaLabels } from "@/constants/analytics"; +import { getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper"; import { headerKeys } from "@/constants/header-keys"; import axiosResponseInterceptorMessages from "@/constants/axios-response-interceptor-messages.js"; @@ -65,7 +66,7 @@ export default { const headers = { [headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings), [headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME, - [headerKeys.SESSION_SEQUENCE_NUMBER]: store.getters.applicationUser?.savedSessionId, + [headerKeys.SESSION_SEQUENCE_NUMBER]: getSessionKeyValue(), [headerKeys.REFERRAL_SEQUENCE_NUMBER]: order?.referralSequenceNumber, [headerKeys.TRANSACTION_ID]: crypto.randomUUID(), [headerKeys.EON]: order?.eon, diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index 208a301e4..e510959db 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -109,8 +109,10 @@ export async function getImplicitNavigation(toRoute) { export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLog }) { const log = getQuerystringParameter(queryStrings.LOG); if (eval(log)) { + console.log("------------- Navigate to heritage start -----------------"); console.log(new Date() + " applicationConfig: " + JSON.stringify(applicationConfig)); console.log(new Date() + " navigateToHeritageFunnel: " + applicationConfig.HERITAGE_FUNNEL); + console.log("------------- Navigate to heritage end -----------------"); } // Create the order (or save existing order) when navigating to Heritage Funnel. @@ -132,6 +134,8 @@ export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLo } if ( + (process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglasstest") && + process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") || (process.env.VUE_APP_HERITAGE_FUNNEL.includes("fixmyglassdev") && process.env.VUE_APP_CURRENT_ENVIRONMENT === "Localhost") || (process.env.VUE_APP_HERITAGE_FUNNEL.includes("localhost") && diff --git a/src/helpers/recal-helper.js b/src/helpers/recal-helper.js new file mode 100644 index 000000000..c73755e8f --- /dev/null +++ b/src/helpers/recal-helper.js @@ -0,0 +1,60 @@ +import { deepClone } from "@/helpers/object-helper"; +import { partTypeStrings } from "@/constants/part-type-strings"; + +const recalPartTypes = [partTypeStrings.RECALIBRATION, partTypeStrings.ADAS_RECALIBRATION]; + +export function isRecalPartOrHasChildRecalPart(lineItem) { + if (lineItem.childParts && lineItem.childParts.length > 0) { + return isRecalPart(lineItem) || containsRecalParts(lineItem.childParts); + } else { + return isRecalPart(lineItem); + } +} + +export function isRecalPart(lineItem) { + return recalPartTypes.some((type) => lineItem.partType === type); +} + +export function containsRecalParts(lineItems) { + if (!lineItems) { + return false; + } + + if (Array.isArray(lineItems)) { + return lineItems.some((li) => isRecalPartOrHasChildRecalPart(li)); + } else { + // complex object form -- flatten and re-call. + const flattened = [ + ...(lineItems.glassParts ?? []), + ...(lineItems.supportingItems ?? []), + ...(lineItems.vaps ?? []), + ...(lineItems.promos ?? []), + ]; + + return flattened.some((li) => isRecalPartOrHasChildRecalPart(li)); + } +} + +export function getItemsWithoutRecalParts(lineItems) { + const copy = deepClone(lineItems); + + const firstLevelFiltered = copy.filter((li) => !isRecalPart(li)); + + const childrenFiltered = firstLevelFiltered.map((li) => { + if (li.childParts && li.childParts.length > 0) { + li.childParts = getItemsWithoutRecalParts(li.childParts); + } + + return li; + }); + + return childrenFiltered; +} + +export function getTopLevelPartsWithRecal(lineItems) { + if (!lineItems) { + return null; + } + + return lineItems.filter((li) => isRecalPartOrHasChildRecalPart(li)); +} diff --git a/src/helpers/service-package-helper.js b/src/helpers/service-package-helper.js index 310a87e0e..6ff937564 100644 --- a/src/helpers/service-package-helper.js +++ b/src/helpers/service-package-helper.js @@ -239,6 +239,15 @@ export function getPackageNameByType(packageType) { return package_names[packageType] || null; } +export function getDiscountPackageName(discountPackage) { + const package_names = { + ECON: packageNames.TIER_ONE, + STANDARD: packageNames.TIER_TWO, + PREMIUM: packageNames.TIER_THREE, + }; + return package_names[discountPackage] || null; +} + function maxTier(tierA, tierB) { if (tierA === packageNames.TIER_THREE || tierB === packageNames.TIER_THREE) { return packageNames.TIER_THREE; diff --git a/src/layouts/address-lookup/address-lookup.vue b/src/layouts/address-lookup/address-lookup.vue index 403982a83..6ac7a3d9a 100644 --- a/src/layouts/address-lookup/address-lookup.vue +++ b/src/layouts/address-lookup/address-lookup.vue @@ -375,6 +375,7 @@ export default { }, false ); + await this.getAndSaveBillToAccountNumber(); return await this.navigateForward(carsFound); }, diff --git a/src/layouts/capability-questions/capability-questions.vue b/src/layouts/capability-questions/capability-questions.vue index a94db9786..4d637c978 100644 --- a/src/layouts/capability-questions/capability-questions.vue +++ b/src/layouts/capability-questions/capability-questions.vue @@ -24,6 +24,7 @@ import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; import store from "@/store"; +import { storeActions } from "@/constants/store-actions"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { Form, defineRule } from "vee-validate"; import { required } from "@/helpers/validation-rules"; @@ -52,9 +53,20 @@ export default { const resultMap = await settleAllPromises(promiseResultMap); // Call the "next" function to complete the transition to this page. - next((vm) => { + next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); if (store.getters.externalParameterState?.isExternalParameter) { + if (store.getters.externalParameterServiceZip.zipCode) { + await baseMixin.methods.dispatchStoreAction( + storeActions.SAVE_SERVICE_ZIP_CODE_INFO, + { + zipCode: store.getters.externalParameterServiceZip.zipCode, + state: null, + zipCodeCtu: null, + }, + false + ); + } baseMixin.methods.ResetExternalParamsAndHideModal(); } }); diff --git a/src/layouts/confirmation/confirmation.spec.js b/src/layouts/confirmation/confirmation.spec.js index 166973ce6..4bbe162d7 100644 --- a/src/layouts/confirmation/confirmation.spec.js +++ b/src/layouts/confirmation/confirmation.spec.js @@ -237,7 +237,7 @@ describe("computed properties...", () => { const testValue = wrapper.vm.AppointmentWordingText; // Assert - expect(testValue).toEqual("wording Text 123, test,
test, AZ 12345"); + expect(testValue).toEqual("wording Text test, 123,
test, AZ 12345"); }); test("AppointmentWordingText should return DROP_OFF text in expected format.", () => { //Arrange @@ -419,7 +419,7 @@ describe("computed properties...", () => { const testValue = wrapper.vm.ServiceLocationFullAddress; // Assert - expect(testValue).toEqual("123, test,
test, AZ 12345"); + expect(testValue).toEqual("test, 123,
test, AZ 12345"); }); test("ProviderFullAddress should return text in expected format.", () => { //Arrange diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 72ee038db..2f499122d 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -60,10 +60,13 @@ v-model="lineItems" :showAsPaid="isPia" servicePackageOptionsCmsName="ServicePackageTitle" + :isInsurance="isInsurance" :insuranceDeductible="currentDeductible" :insuranceCompanyName="insuranceCompanyName" :showInsuranceCoverageAs="showInsuranceCoverageAs" - :shouldHideRecalibration="shouldHideRecalibration" /> + :shouldHideRecalibration="shouldHideRecalibration" + :isItac="isItac" + :isNoComp="isNoComp" />
@@ -108,6 +111,7 @@ import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helpe import { experimentSettings } from "@/constants/experiments"; import { partTypeStrings } from "@/constants/part-type-strings"; import { containsLineItemWithPartType } from "@/helpers/service-package-helper"; +import { containsRecalParts } from "@/helpers/recal-helper.js"; export default { name: "confirmation", @@ -170,10 +174,7 @@ export default { }, computed: { isRecalibrationOnOrder() { - return containsLineItemWithPartType( - partTypeStrings.RECALIBRATION, - this?.lineItems?.supportingItems - ); + return containsRecalParts(this?.lineItems); }, shouldHideRecalibration() { return ( @@ -313,11 +314,7 @@ export default { } }, ServiceLocationFullAddress() { - return `${this.ServiceLocationAddress2 ? this.ServiceLocationAddress2 + "," : ""} ${ - this.ServiceLocationAddress - },
${this.ServiceLocationCity}, ${this.ServiceLocationState} ${ - this.ServiceLocationZipCode - }`; + return `${this.ServiceLocationAddress}${this.ServiceLocationAddress2 ? ", " + this.ServiceLocationAddress2 : ""},
${this.ServiceLocationCity}, ${this.ServiceLocationState} ${this.ServiceLocationZipCode}`; }, ProviderFullAddress() { return `${this.ProviderAddress},
${this.ProviderCity}, ${this.ProviderState} ${this.ProviderZipCode}`; diff --git a/src/layouts/insurance-company/insurance-company.vue b/src/layouts/insurance-company/insurance-company.vue index bc9e5755a..a13755ea0 100644 --- a/src/layouts/insurance-company/insurance-company.vue +++ b/src/layouts/insurance-company/insurance-company.vue @@ -74,6 +74,10 @@ export default { // Call the "next" function to complete the transition to this page. next((vm) => { + //Reload page if serve from bfcache. + if (performance.getEntriesByType("navigation")[0].type === "back_forward") { + vm.handlePageShow(); + } vm.setCmsContent(resultMap.cmsContent); vm.originalList = resultMap.insuranceCompanyList; if (store.getters.externalParameterState?.isExternalParameter) { @@ -93,6 +97,9 @@ export default { }, }, methods: { + handlePageShow() { + window.location.reload(); + }, isCashParentAccountNumber() { return ( applicationConfig.CASH_PARENT_ACCOUNT_NUMBER.toString() === @@ -121,6 +128,20 @@ export default { this.parentAccountNumber.toString(), false ); + + const billToAccountNumber = await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.GET_BILL_TO_ACCOUNT_NUMBER, + {}, + "insurance-company", + false + ); + + baseMixin.methods.dispatchStoreAction( + storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER, + billToAccountNumber, + false + ); + navigateToHeritageFunnel({ shouldSaveSession: true, pageNameToLog: "insurance-company", diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue index 308180d20..2f82691ce 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.vue +++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue @@ -323,6 +323,7 @@ export default { false ); + await this.getAndSaveBillToAccountNumber(); return await this.navigateForward(); }, lookupVin(plate, state) { diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index e2838c27e..00df8c04b 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -24,6 +24,7 @@ import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; import store from "@/store"; +import { storeActions } from "@/constants/store-actions"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { Form, defineRule } from "vee-validate"; import { required } from "@/helpers/validation-rules"; @@ -52,9 +53,21 @@ export default { const resultMap = await settleAllPromises(promiseResultMap); // Call the "next" function to complete the transition to this page. - next((vm) => { + next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); if (store.getters.externalParameterState?.isExternalParameter) { + if (store.getters.externalParameterServiceZip.zipCode) { + await baseMixin.methods.dispatchStoreAction( + storeActions.SAVE_SERVICE_ZIP_CODE_INFO, + { + zipCode: store.getters.externalParameterServiceZip.zipCode, + state: null, + zipCodeCtu: null, + }, + false + ); + } + baseMixin.methods.ResetExternalParamsAndHideModal(); } }); diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index f49f5876c..bc1ec308e 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -24,6 +24,7 @@ import questionsPageLayout from "@/fmg-components/layouts/questions-page-layout/ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; import store from "@/store"; +import { storeActions } from "@/constants/store-actions"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { Form, defineRule } from "vee-validate"; import { required } from "@/helpers/validation-rules"; @@ -52,9 +53,22 @@ export default { const resultMap = await settleAllPromises(promiseResultMap); // Call the "next" function to complete the transition to this page. - next((vm) => { + next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); if (store.getters.externalParameterState?.isExternalParameter) { + if (store.getters.externalParameterServiceZip.zipCode) { + const serviceState = store.getters.order.serviceLocation.state; + const serviceZipCtu = store.getters.order.serviceLocation.zipCodeCtu; + await baseMixin.methods.dispatchStoreAction( + storeActions.SAVE_SERVICE_ZIP_CODE_INFO, + { + zipCode: store.getters.externalParameterServiceZip.zipCode, + state: serviceState ?? null, + zipCodeCtu: serviceZipCtu ?? null, + }, + false + ); + } baseMixin.methods.ResetExternalParamsAndHideModal(); } }); diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 3c8d23502..74a3c65c0 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -34,7 +34,9 @@ :insuranceDeductible="currentDeductible" :insuranceCompanyName="insuranceCompanyName" :showInsuranceCoverageAs="showInsuranceCoverageAs" - :shouldHideRecalibration="shouldHideRecalibration" /> + :shouldHideRecalibration="shouldHideRecalibration" + :isItac="isItac" + :isNoComp="isNoComp" />
@@ -76,7 +78,7 @@ validationRules="payment-method-required" /> 0 + ); + }, totalAmountDue() { return baseMixin.methods.getAmountDue(this.lineItems); }, diff --git a/src/layouts/payment/payment.vue b/src/layouts/payment/payment.vue index 64989ec66..de74071c2 100644 --- a/src/layouts/payment/payment.vue +++ b/src/layouts/payment/payment.vue @@ -57,7 +57,9 @@ recyclingModalCmsWidgetName="RecycleModal" :insuranceDeductible="currentDeductible" :insuranceCompanyName="insuranceCompanyName" - :showInsuranceCoverageAs="showInsuranceCoverageAs" /> + :showInsuranceCoverageAs="showInsuranceCoverageAs" + :isItac="isItac" + :isNocomp="isNocomp" />
diff --git a/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue b/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue index 512d0f577..bf9433301 100644 --- a/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue +++ b/src/layouts/quote/afterpay-modal-banner/afterpay-modal-banner.vue @@ -30,7 +30,7 @@ import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper"; import baseMixin from "@/mixins/base-mixin.js"; import { getPromosThatMatchLineItemsOnOrder } from "@/helpers/promotions-helper"; -import { getArrayOfAllLineItems } from "@/store"; +import { getArrayOfAllLineItemsAndChildParts } from "@/store"; const INLINE_IMAGE_TOKEN = "custom:inlineImage"; const AFTERPAY_PRICE_TOKEN = "custom:afterpayPrice"; @@ -76,7 +76,7 @@ export default { return this.getCmsContent(this.cmsWidgetName, "SubheaderText"); }, afterpayPrice() { - let allLineItems = getArrayOfAllLineItems(this.lineItems); + let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems); if (this.lineItems.promos) { allLineItems = allLineItems?.filter((item) => item.partType !== "PROMO_DISCOUNT"); } @@ -91,7 +91,7 @@ export default { }); if (this.lineItems.promos) { - let allLineItems = getArrayOfAllLineItems(this.lineItems); + let allLineItems = getArrayOfAllLineItemsAndChildParts(this.lineItems); const promos = getPromosThatMatchLineItemsOnOrder( this.lineItems.promos, allLineItems diff --git a/src/layouts/quote/quote.spec.js b/src/layouts/quote/quote.spec.js index d9c1ffabf..778ce0ce8 100644 --- a/src/layouts/quote/quote.spec.js +++ b/src/layouts/quote/quote.spec.js @@ -65,6 +65,28 @@ jest.mock("@/mixins/base-mixin", () => ({ }, })); +const mockExperimentSettings = experimentSettings; + +jest.mock("@/mixins/experiment-mixin.js", () => ({ + methods: { + getSettingValue(settingName) { + if (settingName === mockExperimentSettings.SERVICE_PACKAGE_DISCOUNT) { + return true; + } + if ( + settingName === mockExperimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL + ) { + return 300; + } else if ( + settingName === mockExperimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL + ) { + return 500; + } + return "test"; + }, + }, +})); + const mockMixin = { methods: { filterOutFees: jest.fn().mockImplementation(() => { @@ -73,12 +95,6 @@ const mockMixin = { isFormValid: jest.fn().mockImplementation(() => { return true; }), - getSettingValue: jest.fn((settingName) => { - if (settingName === experimentSettings.SERVICE_PACKAGE_DISCOUNT) { - return true; - } - return false; - }), }, }; let mockTierOnePrice = 501; @@ -309,7 +325,7 @@ describe("quote.vue", () => { wrapper.vm.$route = { query: { isInsurance: "false" } }; - const isServicePackageDiscount = mockMixin.methods.getSettingValue( + const isServicePackageDiscount = experimentMixin.methods.getSettingValue( experimentSettings.SERVICE_PACKAGE_DISCOUNT ); @@ -364,7 +380,7 @@ describe("quote.vue", () => { //Assert expect(wrapper.vm.isInsuranceSelected).toBe(true); }); - test("should default to cash if query param 'isInsurance' is false", async () => { + test("should use amount threshold if query param 'isInsurance' is false", async () => { //Arrange store.getters = { lineItems: { @@ -391,6 +407,7 @@ describe("quote.vue", () => { settingName: "SERVICE_PACKAGE_DISCOUNT", }, }; + mockTierOnePrice = 200; const { wrapper } = setupMocks({}); //Act @@ -701,7 +718,7 @@ describe("quote.vue", () => { inactivePromos: [], }, }, - externalParameterState: { isExternalParameter: true }, + externalParameterState: { isExternalParameter: 1 }, externalParameterQuote: { isInsurance: true, servicePackage: "glassonly", @@ -758,8 +775,6 @@ function setupMocks({ customMountOptions }) { baseMixin.methods.ResetExternalParamsAndHideModal = jest.fn(); baseMixin.methods.isFormValid = jest.fn().mockReturnValue(true); mountOptions.global.mocks["$store"] = store; - store.getters.experimentSettings = "test value"; - mountOptions["attachTo"] = document.body; const wrapper = shallowMount(quote, mountOptions); diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 569d70ded..528e76a7b 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -59,7 +59,7 @@ alertClass="alert-info" /> + class="mt-6 quote-disclaimer" /> @@ -135,6 +135,9 @@ import { import { nextTick } from "vue"; import { packageNames } from "@/constants/package-names"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; +import { containsRecalParts } from "@/helpers/recal-helper"; +import { externalParameterStatus } from "@/constants/external-parameters"; + defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); const INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT = 300; @@ -296,8 +299,9 @@ export default { const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => { const serviceLocationState = store.getters.order.serviceLocation.state; - // Override if coming back from QuoteDetails. Remove override after Quote release - const isInsuranceOverrideValue = to.query?.isInsurance; + + // usually true when returning from heritage but can be false when returning from heritage on a save quote + const isInsuranceFromQueryString = to.query?.isInsurance || to.query?.isinsurance; const defaultIsInsuranceSelectedValue = store.getters.order.payment.isInsurance; const hideRecalCost = experimentMixin.methods @@ -308,72 +312,88 @@ export default { if ( serviceLocationState != null && payWithInsuranceStates.find((item) => item === serviceLocationState) - ) + ) { return true; - else if (isInsuranceOverrideValue != null) { - return isInsuranceOverrideValue == "true"; - } else if (defaultIsInsuranceSelectedValue != null) { - return defaultIsInsuranceSelectedValue; - } else { - if (availableLineItems) { - const lineItemsForCalculatingPrice = hideRecalCost - ? baseMixin.methods.filterOutFees( - baseMixin.methods.filterOutRecalibration(availableLineItems) // Strip out recal before filtering out fees - ) - : baseMixin.methods.filterOutFees(availableLineItems); - - return ( - baseMixin.methods.getTierOnePackagePrice(lineItemsForCalculatingPrice) > - insuranceThreshold - ); // compare base price vs arbitrary threshold (representing insurance price) - } else { - return null; - } } + + if (defaultIsInsuranceSelectedValue != null) { + return defaultIsInsuranceSelectedValue; + } + + if (availableLineItems) { + const lineItemsForCalculatingPrice = hideRecalCost + ? baseMixin.methods.filterOutFees( + baseMixin.methods.filterOutRecalibration(availableLineItems) // Strip out recal before filtering out fees + ) + : baseMixin.methods.filterOutFees(availableLineItems); + + if (isInsuranceFromQueryString != null) { + if (isInsuranceFromQueryString.toLowerCase() === "true") { + return true; + } + } + + return ( + baseMixin.methods.getTierOnePackagePrice(lineItemsForCalculatingPrice) > + insuranceThreshold + ); // compare base price vs arbitrary threshold (representing insurance price) + } + + return null; }; + let thresholdToUse = INSURANCE_TAB_TO_DISPLAY_THRESHOLD_DEFAULT; let internalThreshold = experimentMixin.methods.getSettingValue( experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_INTERNAL ); + let externalThreshold = experimentMixin.methods.getSettingValue( + experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL + ); + const isExternalParameter = store.getters.externalParameterState?.isExternalParameter; - if (!store.getters.externalParameterState?.isExternalParameter) { - // there are no external parameters + // prettier-ignore + { + const log = getQuerystringParameter(queryStrings.LOG); + if (eval(log)) { + console.log("------------- quote.vue thresholds start -----------------"); + console.log(new Date() + " isExternalParameter: " + isExternalParameter); + console.log(new Date() + " internalThreshold: " + internalThreshold); + console.log(new Date() + " externalThreshold: " + externalThreshold); + console.log("------------- quote.vue thresholds end -----------------"); + } + } + if (isExternalParameter === externalParameterStatus.NOT_SET) { + // there are no active or inactive external parameters; use internal threshold if (internalThreshold) thresholdToUse = internalThreshold; + vm.isInsuranceSelected = getIsInsuranceSelectedValue( vm.availableLineItems, thresholdToUse ); baseMixin.methods.ResetExternalParamsAndHideModal(); } else { - // there ARE external parameters - - if (store.getters.externalParameterQuote.isInsurance == true) { + // user came from an external source, however, the externalParms may have been reset on service-zip, property-questions, etc... + if (eval(store.getters.externalParameterQuote?.isInsurance)) { // did user intentionally select insurance? vm.isInsuranceSelected = true; vm.servicePackage = store.getters.externalParameterQuote.servicePackage; await nextTick(); - const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); - if (isValid) { - vm.forwardButtonAction(); - } else { - baseMixin.methods.ResetExternalParamsAndHideModal(); - } - } else { - // did user come from external source (LeadGen)? - let externalSource = store.getters.externalParameterSource - ? store.getters.externalParameterSource - : null; - // Business logic to determine what "external" source is - if (externalSource?.includes("LeadGen")) { - const externalThreshold = experimentMixin.methods.getSettingValue( - experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL - ); - if (externalThreshold) thresholdToUse = externalThreshold; - } else { - if (internalThreshold) thresholdToUse = internalThreshold; + if ( + vm.servicePackage != null && + isExternalParameter === externalParameterStatus.ACTIVE + ) { + const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm); + if (isValid) { + baseMixin.methods.ResetExternalParamsAndHideModal(); + vm.forwardButtonAction(); + } } + baseMixin.methods.ResetExternalParamsAndHideModal(); + } else { + if (externalThreshold) thresholdToUse = externalThreshold; + vm.isInsuranceSelected = getIsInsuranceSelectedValue( vm.availableLineItems, thresholdToUse @@ -399,10 +419,7 @@ export default { return Object.assign({}, this.lineItems); }, isRecalibrationOnOrder() { - return containsLineItemWithPartType( - partTypeStrings.RECALIBRATION, - this?.availableLineItems - ); + return store.getters.isRecalibrationOnOrder; }, isServicePackageDiscountOnOrder() { return containsLineItemWithPartType( @@ -417,10 +434,7 @@ export default { ); }, shouldHideRecalibration() { - return ( - this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE)?.toLowerCase() === - "true" && this.isRecalibrationOnOrder - ); + return store.getters.shouldHideRecalibration; }, showAfterpayBanner() { return ( @@ -438,35 +452,21 @@ export default { }, arePagePrerequisitesValid() { const payment = store.getters.order.payment; - const log = getQuerystringParameter(queryStrings.LOG); - if (eval(log)) { - console.log("quote.vue pagePrereqs:"); - console.log( - new Date() + - " store.getters.order.serviceLocation.zipCode: " + - store.getters.order.serviceLocation?.zipCode - ); - console.log( - new Date() + - " store.getters.order.serviceLocation.zipCodeCtu: " + - store.getters.order.serviceLocation?.zipCodeCtu - ); - console.log( - new Date() + - " store.getters.order.damage.isRepair: " + - store.getters.order.damage?.isRepair - ); - console.log( - new Date() + - " store.getters.order.lineItems?.glassParts: " + - JSON.stringify(store.getters.order.lineItems?.glassParts) - ); - console.log( - new Date() + - " store.getters.order.payment.insuranceCoverage?.isVerified: " + - payment?.insuranceCoverage?.isVerified - ); + + // prettier-ignore + { + const log = getQuerystringParameter(queryStrings.LOG); + if (eval(log)) { + console.log("------------- quote.vue pagePrereqs start -----------------"); + console.log(new Date() + " store.getters.order.serviceLocation.zipCode: " + store.getters.order.serviceLocation?.zipCode); + console.log(new Date() +" store.getters.order.serviceLocation.zipCodeCtu: " + store.getters.order.serviceLocation?.zipCodeCtu); + console.log(new Date() + " store.getters.order.damage.isRepair: " + store.getters.order.damage?.isRepair); + console.log(new Date() + " store.getters.order.lineItems?.glassParts: " + JSON.stringify(store.getters.order.lineItems?.glassParts)); + console.log(new Date() + " store.getters.order.payment.insuranceCoverage?.isVerified: " + payment?.insuranceCoverage?.isVerified); + console.log("------------- quote.vue pagePrereqs end -----------------"); + } } + return ( store.getters.order.serviceLocation.zipCode && store.getters.order.serviceLocation.zipCodeCtu && @@ -579,15 +579,14 @@ export default { } }); this.$nextTick(() => { - const availablePackageNames = this.$refs.servicePackage.servicePackageAnswers; + const availablePackageNames = this.$refs.servicePackage?.servicePackageAnswers; var eventLabel = ""; availablePackageNames?.forEach((tier) => { if (tier) { let lineItemsToPrice = this.availableLineItems; if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { - lineItemsToPrice = lineItemsToPrice?.filter((item) => { - return item.partType != partTypeStrings.RECALIBRATION; - }); + lineItemsToPrice = + baseMixin.methods.filterOutRecalibration(lineItemsToPrice); } if (this.isServicePackageDiscountOnOrder) { lineItemsToPrice = lineItemsToPrice?.filter((item) => { @@ -611,12 +610,7 @@ export default { }); eventLabel = eventLabel.slice(0, -1); - this.pushEventToGA( - "quote", - this.GaActions.SERVICE_PACKAGE_PRICE, - eventLabel, - true - ); + this.pushEventToGA("quote", this.GaActions.SERVICE_PACKAGE_PRICE, eventLabel, true); }); }, }, diff --git a/src/layouts/quote/service-package-question/service-package-question-test-helper.js b/src/layouts/quote/service-package-question/service-package-question-test-helper.js index 36f938a7e..a352b08d6 100644 --- a/src/layouts/quote/service-package-question/service-package-question-test-helper.js +++ b/src/layouts/quote/service-package-question/service-package-question-test-helper.js @@ -31,7 +31,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -40,7 +40,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -48,7 +48,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -58,7 +58,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -67,7 +67,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -75,7 +75,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -85,7 +85,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -94,7 +94,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -102,7 +102,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -112,7 +112,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -121,7 +121,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -129,7 +129,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -139,7 +139,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -148,7 +148,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -156,7 +156,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -166,7 +166,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -175,7 +175,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -183,7 +183,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -193,7 +193,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -202,7 +202,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -210,7 +210,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement glass
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement glass
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -220,7 +220,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -229,7 +229,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -237,7 +237,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, @@ -247,7 +247,7 @@ export const mockProcessedCmsContent = { HeaderText: "Economy service", SubheaderText: "", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
", Image: "", FooterText: "{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}", @@ -256,7 +256,7 @@ export const mockProcessedCmsContent = { HeaderText: "Standard service", SubheaderText: "MOST POPULAR", BodyText: - "
  • New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", + "
  • New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
", Image: "", FooterText: "", }, @@ -264,7 +264,7 @@ export const mockProcessedCmsContent = { HeaderText: "Premium service", SubheaderText: "", BodyText: - "
  • {if:custom:badCustomValue}DO NOT SHOW{end}New replacement windshield
  • Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", + "
  • {if:custom:badCustomValue}DO NOT SHOW{end}New replacement windshield
  • Expert installation{if:custom:showRecalInServicePackages} and {textLink:EstimateRecalModal,recalibration}{end}
  • Nationwide lifetime warranty
  • {if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}
  • {if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}
  • {if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}™ treatment{end}
", Image: "", FooterText: "", }, diff --git a/src/layouts/quote/service-package-question/service-package-question.spec.js b/src/layouts/quote/service-package-question/service-package-question.spec.js index a23d1ead6..d8edb254c 100644 --- a/src/layouts/quote/service-package-question/service-package-question.spec.js +++ b/src/layouts/quote/service-package-question/service-package-question.spec.js @@ -10,6 +10,7 @@ import { containsLineItemWithPartType, findLineItemsWithPartType, } from "@/helpers/service-package-helper"; +import { experimentSettings } from "@/constants/experiments"; import baseMixin from "@/mixins/base-mixin.js"; import { partTypeStrings } from "@/constants/part-type-strings"; import { nextTick } from "vue"; @@ -19,6 +20,18 @@ jest.mock("@/store", () => ({ dispatch: jest.fn(), })); +const mockExperimentSettings = experimentSettings; + +jest.mock("@/mixins/experiment-mixin.js", () => ({ + methods: { + getSettingValue(settingName) { + if (settingName === mockExperimentSettings.PROMO_ON_PACKAGE) { + return true; + } + }, + }, +})); + describe("service-package-question.vue", () => { beforeEach(async () => { processedCmsContent = inputQuestionWidgetAnswers; @@ -162,6 +175,19 @@ describe("service-package-question.vue", () => { wrapper.vm.servicePackageAnswers[0].buttonAuxillaryCopy.includes("As little as") ).toBe(true); }); + it("should show service package discount when insurance is not selected", () => { + // Arrange + mockProps.isInsuranceSelected = false; + const wrapper = setupMocks({}); + // Act + + // Assert + expect( + wrapper.vm.servicePackageAnswers[0].additionalButtonData.Text.includes( + "Special: Save $" + ) + ).toBe(true); + }); it("should return [] from nullSafeAvailableLineItems and not error out if availableLineItems is null", () => { // Arrange mockProps.availableLineItems = null; @@ -476,6 +502,8 @@ describe("service-package-question.vue, matching business rules for package disp mockProps.availableLineItems.push(driverFrontWiperLineItem); mockProps.availableLineItems.push(passengerFrontWiperLineItem); mockProps.isRecalibrationOnOrder = true; + mockProps.shouldHideRecalibration = false; + mockProps.showRecalInServicePackages = true; Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]); const wrapper = setupMocks({ mountOptionsMockData: { diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 1a9ed4ed8..7df8dab5f 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -28,6 +28,7 @@ import { containsLineItemWithPartType, findLineItemsWithPartType, getPackageNameByType, + getDiscountPackageName, } from "@/helpers/service-package-helper"; import { getPromosThatMatchLineItemsOnOrder, @@ -35,6 +36,9 @@ import { getPromosWithAddableVaps, getLineItemsThatMatchPromos, } from "@/helpers/promotions-helper"; +import { containsRecalParts, getItemsWithoutRecalParts } from "@/helpers/recal-helper"; +import experimentMixin from "@/mixins/experiment-mixin.js"; +import { experimentSettings } from "@/constants/experiments"; export default { name: "servicePackageQuestion", @@ -128,16 +132,22 @@ export default { buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.SubWidgetName), buttonBodyCopy: this.getBodyTextFromCms(answer.SubWidgetName), //The package supports service-package-discount if it has the text value in CMS - buttonAuxillaryCopy: this.getDiscountedPackagePriceString( - answer.Name, - this.isServicePackageDiscountOnOrder && answer.Text != "" - ), + buttonAuxillaryCopy: this.isInsuranceSelected + ? this.getDiscountedPackagePriceString(answer.Name, false) + : this.getDiscountedPackagePriceString( + answer.Name, + this.isServicePackageDiscountOnOrder && + this.discountPackageNames == answer.Name + ), buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName), additionalButtonData: { strikeThroughPrice: this.getPackagePriceString(answer.Name), servicePackageDiscount: - this.isServicePackageDiscountOnOrder && answer.Text != "", - Text: answer.Text + this.getServicePackageDiscountPrice(), + this.isServicePackageDiscountOnOrder && + this.discountPackageNames == answer.Name, + Text: this.isInsuranceSelected + ? false + : "Special: Save $" + this.getServicePackageDiscountPrice(), }, })); return modifiedAnswers; @@ -148,6 +158,12 @@ export default { this.nullSafeAvailableLineItems ); }, + discountPackageNames() { + const discountServicePackage = experimentMixin.methods.getSettingValue( + experimentSettings.PROMO_ON_PACKAGE + ); + return getDiscountPackageName(discountServicePackage); + }, frontWipersApplicableForTierTwo() { return shouldFrontWipersBeAvailable( this.glassToReplace, @@ -200,6 +216,9 @@ export default { this.nullSafeAvailableLineItems ); }, + showRecalInServicePackages() { + return this.isRecalibrationOnOrder && !this.shouldHideRecalibration; + }, }, methods: { processIfStatements, @@ -239,9 +258,7 @@ export default { let lineItemsToPrice = [...this.nullSafeAvailableLineItems]; if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) { - lineItemsToPrice = lineItemsToPrice.filter((item) => { - return item.partType != partTypeStrings.RECALIBRATION; - }); + lineItemsToPrice = getItemsWithoutRecalParts(lineItemsToPrice); } //remove service package discount part @@ -358,8 +375,8 @@ export default { }, getCustomValueFromString(str) { switch (str) { - case "isRecalibrationOnOrder": - return this.isRecalibrationOnOrder; + case "showRecalInServicePackages": + return this.showRecalInServicePackages; case "frontWipersApplicableForTierTwo": return this.frontWipersApplicableForTierTwo; case "rearWiperApplicableForTierTwo": diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index b0e10df95..97037c4e9 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -1,15 +1,8 @@