Merge remote-tracking branch 'origin/develop' into feature/CSR-2266

This commit is contained in:
Scott Kiener 2024-10-28 15:50:11 -04:00
commit fcd5c99cf3
11 changed files with 155 additions and 62 deletions

View file

@ -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",

View file

@ -66,7 +66,7 @@ export default {
},
false
);
}
}
baseMixin.methods.ResetExternalParamsAndHideModal();
}
});

View file

@ -46,10 +46,19 @@ import baseMixin from "@/mixins/base-mixin.js";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { applicationConfig } from "@/constants/application-config";
import store from "@/store";
import router from "@/router";
export default {
name: "insurance-company",
async beforeRouteEnter(to, from, next) {
//force reload if redirected from external application
if (from.redirectedFrom === undefined && !sessionStorage.getItem("reloaded")) {
sessionStorage.setItem("reloaded", "true");
router.go(0);
} else {
sessionStorage.removeItem("reloaded");
}
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
const insuranceCompanyListPromise = baseMixin.methods.dispatchStoreActionWithLogging(

View file

@ -66,7 +66,7 @@ export default {
},
false
);
}
}
baseMixin.methods.ResetExternalParamsAndHideModal();
}
});

View file

@ -346,12 +346,24 @@ export 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 (
isExternalParameter === externalParameterStatus.NOT_SET ||
isExternalParameter === externalParameterStatus.INACTIVE
) {
// 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;
@ -361,23 +373,26 @@ export default {
);
baseMixin.methods.ResetExternalParamsAndHideModal();
} else {
// user came from an external source
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();
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 {
const externalThreshold = experimentMixin.methods.getSettingValue(
experimentSettings.INSURANCE_TAB_TO_DISPLAY_THRESHOLD_EXTERNAL
);
thresholdToUse = externalThreshold;
if (externalThreshold) thresholdToUse = externalThreshold;
vm.isInsuranceSelected = getIsInsuranceSelectedValue(
vm.availableLineItems,
@ -437,7 +452,7 @@ export default {
},
arePagePrerequisitesValid() {
const payment = store.getters.order.payment;
// prettier-ignore
{
const log = getQuerystringParameter(queryStrings.LOG);

View file

@ -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;

View file

@ -36,6 +36,8 @@ import {
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",
@ -129,16 +131,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;
@ -149,6 +157,21 @@ export default {
this.nullSafeAvailableLineItems
);
},
discountPackageNames() {
const discountServicePackage = experimentMixin.methods.getSettingValue(
experimentSettings.PROMO_ON_PACKAGE
);
switch (discountServicePackage) {
case "ECON":
return packageNames.TIER_ONE;
case "STANDARD":
return packageNames.TIER_TWO;
case "PREMIUM":
return packageNames.TIER_THREE;
default:
return null;
}
},
frontWipersApplicableForTierTwo() {
return shouldFrontWipersBeAvailable(
this.glassToReplace,

View file

@ -1,15 +1,8 @@
<template>
<div
:class="[this.additionalButtonData.Text ? 'discount-text' : '']"
v-if="this.additionalButtonData.servicePackageDiscount"
v-html="this.additionalButtonData.Text"></div>
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
<div
class="package-label"
:class="[
this.buttonLabelSubCopy ? 'has-subheader' : '',
this.additionalButtonData.servicePackageDiscount ? 'has-text' : '',
]"
:class="[this.buttonLabelSubCopy ? 'has-subheader' : '']"
for="testradio">
<div class="package-specs">
<div v-if="this.additionalButtonData.servicePackageDiscount" class="row">
@ -82,6 +75,15 @@
v-if="this.buttonFooterCopy"
v-html="this.buttonFooterCopy"></div>
</div>
<div
v-if="
this.additionalButtonData.servicePackageDiscount &&
this.additionalButtonData.Text
"
class="special-save-box">
<span v-html="this.additionalButtonData.Text"></span>
</div>
</div>
</div>
</baseInputButton>
@ -143,20 +145,13 @@ export default {
margin: 1rem 0.5rem;
display: flex;
flex-direction: column;
align-self: flex-end;
}
label {
display: block;
@include media-breakpoint-up(md) {
display: flex;
min-height: 336px;
}
//Unique breakpoint for this page only to accommodate price spacing issues
//from use of position: absolute on .service-package-discount (per Design Team request)
@media only screen and (min-width: 860px) {
display: flex;
min-height: 276px;
height: 100%;
}
}
@ -179,19 +174,16 @@ export default {
border-radius: 0.5rem;
overflow: hidden;
min-height: 60px;
max-height: 100px;
max-height: 126px;
@include media-breakpoint-up(md) {
max-height: 500px;
padding-bottom: 3rem;
}
transition: max-height 0.25s ease-in;
&.has-subheader {
min-height: 80px;
}
&.has-text {
border-radius: 0 0 0.5rem 0.5rem;
border: 1px solid $green;
}
&:before {
content: "";
@ -245,6 +237,9 @@ export default {
background-color: $blue-100;
border: 1px solid $blue;
max-height: 500px;
.special-save-box {
background-color: $green-200;
}
}
+ .package-label {
&:before {
@ -294,8 +289,8 @@ export default {
bottom: 0.75rem;
left: 0;
width: 100%;
text-align: center;
font-size: 1.125rem;
margin-left: 1.25rem;
font-weight: 600;
}
span.strikethrough-price {
text-decoration: line-through;
@ -316,28 +311,32 @@ export default {
&.service-package-discount {
color: $green;
text-align: end;
font-weight: 500;
font-size: 13px;
font-weight: 600;
font-size: 0.875rem;
display: flex;
justify-content: space-between;
div.strikethrough-discount-price {
text-decoration: line-through;
margin-right: 0;
font-weight: 400;
font-size: 0.875rem;
margin-right: 0.5rem;
font-weight: 600;
font-size: 0.75rem;
line-height: 20px;
color: $gray-500;
padding-right: 2px;
color: $gray-550;
@include media-breakpoint-up(md) {
margin-right: 0.5rem;
font-size: 0.75rem;
}
}
div.discount-price {
color: $green;
font-weight: 600;
font-size: 1.625rem;
}
@include media-breakpoint-up(md) {
position: absolute;
bottom: 0.75rem;
left: 0;
width: 100%;
text-align: center;
left: 0.5rem;
display: flex;
align-items: center;
justify-content: flex-start;
}
}
}
@ -388,6 +387,21 @@ export default {
font-weight: 600;
letter-spacing: 0.75px;
}
.special-save-box {
background-color: $green-100;
display: flex;
flex-direction: row;
margin: auto;
margin-left: -2rem;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
margin-top: 0.5rem;
span {
color: $green;
font-size: 0.875rem;
font-weight: 600;
}
}
}
}
</style>

View file

@ -669,6 +669,12 @@ function updateExternalParameterState() {
if (externalParameterZipCode) {
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_ZIP_CODE, externalParameterZipCode);
}
if (externalParameterEmail) {
store.commit(
storeMutations.UPDATE_EXTERNAL_PARAMETER_EMAIL_ADDRESS,
externalParameterEmail
);
}
if (externalParameterIsInsurance?.toUpperCase() == "TRUE") {
store.commit(storeMutations.UPDATE_EXTERNAL_PARAMETER_IS_INSURANCE, true);
store.commit(

View file

@ -505,7 +505,6 @@ export const mutations = {
saveExternalParameterState(externalParameterState);
},
resetExternalParameterQuoteState(state) {
externalParameterState.quote.isInsurance = null;
externalParameterState.quote.servicePackage = null;
saveExternalParameterState(externalParameterState);
},
@ -1656,7 +1655,7 @@ export const actions = {
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
const referralSequenceNumber = context.getters.order.referralSequenceNumber;
var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&referralSequenceNumber=${referralSequenceNumber}`;
var endPoint = `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&parentAccountNumber=${parentAccountNumber}&referralSequenceNumber=${referralSequenceNumber}&applicationName=${applicationConfig.ANALYTICS_APPLICATION_NAME}`;
if (lineItems) {
endPoint += `&${lineItems}`;
}

View file

@ -6,7 +6,7 @@ process.env.VUE_APP_CURRENT_ENVIRONMENT = "Localhost";
process.env.VUE_APP_MY_ACCOUNT = "https://myaccountdev.safelite.com/";
//process.env.VUE_APP_SAFELITE_HOP = "http://localhost:60966/fmgCheckoutShared.aspx";
process.env.VUE_APP_SAFELITE_HOP = "https://sv2-safelitehop-sys.safelite.com/fmgCheckoutShared.aspx";
process.env.VUE_APP_SAFELITE_HOP = "https://sv2-safelitehop-dev.safelite.com/fmgCheckoutShared.aspx";
// GA & GTM
process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY =