Merge branch 'CSR-1891-abstract-autocomplete-to-component' of https://github.com/Safelite/DigitalConsumer.FixMyGlass into CSR-1891-abstract-autocomplete-to-component

This commit is contained in:
Bryan Mauger 2024-03-25 15:51:54 -04:00
commit 132db34c59
14 changed files with 629 additions and 330 deletions

View file

@ -49,6 +49,7 @@ const storeMutations = {
UPDATE_BILL_TO_ACCT_NUMBER: "updateBillToAcctNumber",
UPDATE_EON: "updateEON",
UPDATE_IS_INSURANCE: "updateIsInsurance",
UPDATE_IS_VERIFIED_INSURANCE: "updateInsuranceVerifiedStatus",
UPDATE_SAVED_SESSION_ID: "updateSavedSessionId",
UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId",
UPDATE_IS_PIA: "updateIsPia",

View file

@ -33,9 +33,10 @@
:key="i"
class="packaged-cart-item">
<span
><span v-if="cartItem.category === 'promos'"></span>{{ cartItem.name
}}<span v-if="cartItem.category === 'promos'"></span
></span>
><span :class="cartItem.category === 'promos' ? 'promo' : ''">{{
cartItem.name
}}</span></span
>
<textLink
v-if="allowItemRemoval"
ref="removeLink"
@ -102,15 +103,18 @@
ref="promoModalQuestion"
class="small mt-4"
v-model="lineItems"
:availableVaps="availableVaps"
:addableVaps="availableVaps"
:pageName="pageName"
modalWidgetName="PromoModalWidget" />
</div>
</div>
<div
class="my-2 lh-1 applied-promo"
class="my-2 lh-1 applied-promo-tag"
v-for="(promoCode, i) in this.getPromoCodeList()"
:key="i">
<span class="caption">Promo code {{ promoCode }} applied </span>
<span class="caption"
>Promo code <span class="promo-code">{{ promoCode }}</span> applied
</span>
</div>
<contentGroupModal ref="RecycleModal" cmsWidgetName="RecycleModal">
<textBlock cmsWidgetName="RecycleTextBlock" />
@ -145,6 +149,7 @@ export default {
damage: Object,
servicePackageOptionsCmsName: String,
availableVaps: Object,
pageName: String,
allowItemRemoval: Boolean,
recyclingModalCmsWidgetName: String,
showAsPaid: Boolean,
@ -917,6 +922,9 @@ export default {
&:last-child {
justify-content: flex-end;
}
&.promo {
text-transform: uppercase;
}
}
a {
font-size: 0.875rem;
@ -1003,13 +1011,18 @@ export default {
text-decoration: none;
}
}
.applied-promo {
.applied-promo-tag {
span {
color: $green-700;
font-weight: 500;
padding: 2px 8px;
background-color: $green-100;
border-radius: 12px;
&.promo-code {
text-transform: uppercase;
padding: 0;
}
}
}
}

View file

@ -51,7 +51,9 @@
alertClass="alert-danger"
v-bind:isDismissible="false" />
<div class="mt-4" v-for="(promoCode, i) in this.getPromoCodeList()" :key="i">
<span class="applied-promo">Promo code "{{ promoCode }}" applied </span>
<span class="applied-promo"
>Promo code <span class="promo-code">{{ promoCode }}</span> applied
</span>
<textLink
ref="removeLink"
linkType="text"
@ -201,7 +203,7 @@ export default {
return {
isValid: !("errorCode" in promoValidationResponse),
promoCode: promoValidationResponse?.orderPromos,
promoCode: promoValidationResponse?.orderPromos, // should be an array of promos
errorCode: promoValidationResponse?.errorCode,
additionalInfo: promoValidationResponse?.additionalInfo,
};
@ -264,7 +266,7 @@ export default {
if (promoCodeData.isValid) {
if (this.taxPromos) {
const pricedLineItemsToTax = [];
pricedLineItemsToTax.push(...promoCodeData.promoCode);
pricedLineItemsToTax.push(...promoCodeData.promoCode); // promoCodeData.promoCode should be an array
const taxedLineItems =
await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
@ -343,6 +345,10 @@ export default {
font-weight: 500;
line-height: 24px;
color: #0c7e47;
.promo-code {
text-transform: uppercase;
}
}
.alert {
color: #d4281c;

View file

@ -100,7 +100,7 @@ export default {
// Do not route to error logic when no wipers found or no promo found (404s)
if (error.response.status != "404") {
router.navigateError();
//router.navigateError(); Emergency change for testing - skiener 03/12
}
global.$logger.logError(

View file

@ -12,38 +12,93 @@ import { includesWindshieldReplacement } from "@/helpers/damage-helper";
import store from "@/store";
import router from "@/router";
/*
If the user has visited the funnel before this method will determine the bets place to
drop them so they don't start at the beginning again. This method will return 'heritage' if
the user has an existing order and they come back in from the Safelite.com CTA.
*/
export async function getPageToRouteExistingOrderTo(toRoute = {}) {
const fmgPage = getQuerystringParameter(queryStrings.FMG_PAGE.toLowerCase());
if (fmgPage && fmgPage === fmgPageValues.SERVICE_LOCATION) {
const serviceLocationComponent = await getLazyLoadedComponent(
fmgPageValues.SERVICE_LOCATION
);
if (serviceLocationComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.SERVICE_LOCATION;
const fmgQueryPage = toRoute.query[queryStrings.FMG_PAGE];
const hasFmgQueryPage = !!fmgQueryPage;
if (hasFmgQueryPage) {
return await getDirectNavigation(toRoute);
} else {
return await getImplicitNavigation(toRoute);
}
}
/*
* Send the user to the specified page, except in specific scenarios.
*/
export async function getDirectNavigation(toRoute) {
const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
// Verified insurance users need to be routed around some pages
// vehicle -> vehicle-damage
// quote -> vin-lookup/estimate
// insurance-company -> vin-lookup/estimate
if (store.getters.payment.insuranceCoverage.isVerified) {
if (fmgPageValue === fmgPageValues.VEHICLE) {
return fmgPageValues.VEHICLE_DAMAGE;
}
if (
fmgPageValue === fmgPageValues.QUOTE ||
fmgPageValue === fmgPageValues.INSURANCE_COMPANY
) {
const skipVin = await skipVinLookup();
if (skipVin) {
return fmgPageValues.ESTIMATE;
} else {
return fmgPageValues.VIN_LOOKUP;
}
}
}
// If the user is coming in via the Safelite.Com CTA
if (toRoute.query[queryStrings.START_TYPE] === "fmg") {
const latestPageRoute = await getLatestPageForRedirection();
return latestPageRoute;
}
// Otherwise, respect navigation from query string.
return fmgPageValue;
}
// If navigating to a specific page, and that page is not part of the vin pages.
// Return that page, so that it can navigate like normal.
if (toRoute.query[queryStrings.FMG_PAGE] !== undefined && !isVinRelatedPage(toRoute)) {
return overrideYmmsDirectionIfNeeded(toRoute);
}
/*
* Determine which page in the flow the user should be sent to, based on their current order.
* General strategy: run through pages in reverse order, and select first where prerequisites are valid.
* QUOTE is the latest page to send to.
*/
export async function getImplicitNavigation(toRoute) {
const vehicleDamageComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_DAMAGE);
const estimateComponent = await getLazyLoadedComponent(fmgPageValues.ESTIMATE);
const vinLookupComponent = await getLazyLoadedComponent(fmgPageValues.VIN_LOOKUP);
const partQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.PART_QUESTIONS);
const vehiclePartsComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_PARTS);
const moldingQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.MOLDING_QUESTIONS);
const capabilityQuestionsComponent = await getLazyLoadedComponent(
fmgPageValues.CAPABILITY_QUESTIONS
);
const quoteComponent = await getLazyLoadedComponent(fmgPageValues.QUOTE);
// If this is not a direct link to a page using fmgPage, not from Safelite.com CTA or this is a vin related page.
// Get the latest page for redirection.
const latestPageRoute = await getLatestPageForRedirection();
return latestPageRoute;
const skipVin = await skipVinLookup();
if (quoteComponent.methods.arePagePrerequisitesValid()) {
// Do not send to quote if verified insurance user.
if (store.getters.payment.insuranceCoverage.isVerified) {
return fmgPageValues.HERITAGE;
} else {
return fmgPageValues.QUOTE;
}
} else if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.CAPABILITY_QUESTIONS;
} else if (moldingQuestionsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.MOLDING_QUESTIONS;
} else if (vehiclePartsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.VEHICLE_PARTS;
} else if (partQuestionsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.PART_QUESTIONS;
} else if (vinLookupComponent.methods.arePagePrerequisitesValid() && !skipVin) {
return fmgPageValues.VIN_LOOKUP;
} else if (estimateComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.ESTIMATE;
} else if (vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.VEHICLE_DAMAGE;
} else {
return fmgPageValues.VEHICLE;
}
}
/*
@ -114,96 +169,6 @@ export async function skipVinLookupNotRepair() {
);
}
/*
Logic for getting the last "valid" page a user visited.
*/
async function getLatestPageForRedirection() {
// If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
// This also works if a user has a 'fmg' start_type query string but no current order.
// That shouldn't happen, but it's possible.
const vehicleDamageComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_DAMAGE);
const estimateComponent = await getLazyLoadedComponent(fmgPageValues.ESTIMATE);
const vinLookupComponent = await getLazyLoadedComponent(fmgPageValues.VIN_LOOKUP);
const partQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.PART_QUESTIONS);
const vehiclePartsComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_PARTS);
const moldingQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.MOLDING_QUESTIONS);
const capabilityQuestionsComponent = await getLazyLoadedComponent(
fmgPageValues.CAPABILITY_QUESTIONS
);
const quoteComponent = await getLazyLoadedComponent(fmgPageValues.QUOTE);
const serviceLocationComponent = await getLazyLoadedComponent(fmgPageValues.SERVICE_LOCATION);
const scheduleComponent = await getLazyLoadedComponent(fmgPageValues.SCHEDULE);
const skipVin = await skipVinLookup();
if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.VEHICLE;
} else if (!estimateComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.VEHICLE_DAMAGE;
} else {
if (quoteComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.QUOTE;
} else if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.CAPABILITY_QUESTIONS;
} else if (moldingQuestionsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.MOLDING_QUESTIONS;
} else if (vehiclePartsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.VEHICLE_PARTS;
} else if (partQuestionsComponent.methods.arePagePrerequisitesValid()) {
return fmgPageValues.PART_QUESTIONS;
} else if (
// capture vin
vinLookupComponent.methods.arePagePrerequisitesValid() &&
!skipVin
) {
return fmgPageValues.VIN_LOOKUP;
} else {
// do not capture vin
return fmgPageValues.ESTIMATE;
}
}
}
/*
Overrides functionality to go to the YMMS pages in certain cases.
If this is not one of the cases, it returns the 'to' fmgPage value.
*/
/* istanbul ignore next */
function overrideYmmsDirectionIfNeeded(toRoute) {
const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
if (store.getters.payment.insuranceCoverage.isVerified) {
switch (fmgPageValue) {
case fmgPageValues.VEHICLE: {
return fmgPageValues.VEHICLE_DAMAGE;
}
default: {
return fmgPageValue;
}
}
} else {
return fmgPageValue;
}
}
/*
Determine if the page is a vin related page.
*/
/* istanbul ignore next */
function isVinRelatedPage(toRoute) {
const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
return (
fmgPageValue === fmgPageValues.VIN_LOOKUP ||
fmgPageValue === fmgPageValues.LICENSE_PLATE_LOOKUP ||
fmgPageValue === fmgPageValues.ADDRESS_LOOKUP ||
fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
fmgPageValue === fmgPageValues.ESTIMATE
);
}
async function getLazyLoadedComponent(pageName) {
return (await lazyLoadComponent(pageName)()).default;
}

View file

@ -1,7 +1,4 @@
import {
getPageToRouteExistingOrderTo,
navigateToHeritageFunnel,
} from "@/helpers/heritage-integration/navigation-helper";
import * as navigationHelper from "@/helpers/heritage-integration/navigation-helper";
import * as orderHelper from "@/helpers/heritage-integration/order-helper";
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
import { storeActions } from "@/constants/store-actions";
@ -11,236 +8,516 @@ import { externalUrls } from "@/router/router-constants/externalUrl-values";
import { queryStrings } from "@/constants/query-strings";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { damageLocationsSelected as glassLocations } from "@/constants/damage-locations-selected";
import store from "@/store";
import router from "@/router";
const getPageToRouteExistingOrderTo = navigationHelper.getPageToRouteExistingOrderTo;
const navigateToHeritageFunnel = navigationHelper.navigateToHeritageFunnel;
// Mock Lazy Load
jest.mock("@/router/dynamic-routing/component-loader.js", () => ({
lazyLoadComponent: jest.fn(),
}));
describe("getPageToRouteExistingOrderTo", () => {
test("should return vehicle", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.VEHICLE);
});
test("should return vehicle-damage", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
});
test("user has YMMS and no vehicle questions > should return estimate", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
const mockExperimentsList = [
{
universeName: "ConceptFunnel",
settings: {
SuppressVinCapture: false,
describe("Explicit vs. Implicit Routing", () => {
test("Should call explicit routing if `fmgPage` parameter is present in query", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: "schedule",
},
},
];
store.commit(storeMutations.UPDATE_EXPERIMENTS, mockExperimentsList);
};
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: false,
[fmgPageValues.VIN_LOOKUP]: true,
mockLazyLoadComponentReturnValues();
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
// If direct, should be "schedule".
// Otherwise, not.
expect(result).toBe("schedule");
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
test("Should call implicit routing if `fmgPage` parameter is NOT present in query", async () => {
// Arrange
const toRoute = {
query: {},
};
//Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
mockLazyLoadComponentReturnValues();
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
// If direct, should be "schedule".
// Otherwise, not.
expect(result).not.toBe("schedule");
});
});
test("user has YMMS but no questions or carId > should return estimate", async () => {
// Arrange
const toRoute = {
query: {},
};
describe("Implicit Routing", () => {
test("should return vehicle", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: false,
[fmgPageValues.VIN_LOOKUP]: false,
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.VEHICLE);
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
test("should return vehicle-damage", async () => {
// Arrange
const toRoute = {
query: {},
};
//Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
});
test("user has YMMS and no vehicle questions > should return estimate", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
const mockExperimentsList = [
{
universeName: "ConceptFunnel",
settings: {
SuppressVinCapture: false,
},
},
];
store.commit(storeMutations.UPDATE_EXPERIMENTS, mockExperimentsList);
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: false,
[fmgPageValues.VIN_LOOKUP]: true,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
});
test("user has YMMS but no questions or carId > should return estimate", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: false,
[fmgPageValues.VIN_LOOKUP]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
});
test("user has capability questions and molding questions > should return capability questions", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: true,
[fmgPageValues.MOLDING_QUESTIONS]: true,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: false,
[fmgPageValues.VIN_LOOKUP]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.CAPABILITY_QUESTIONS);
});
test("user has molding questions and part questions > should return molding questions", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: true,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.MOLDING_QUESTIONS);
});
test("user has vehicle parts questions > should return vehicle-parts", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: true,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.VEHICLE_PARTS);
});
test("user has part questions > should return part-questions", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.PART_QUESTIONS);
});
test("should return quote, if not verified insurance", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
[fmgPageValues.QUOTE]: true,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.QUOTE);
});
test("should not return pages beyond quote even if prerequisites are met", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
[fmgPageValues.QUOTE]: true,
[fmgPageValues.SERVICE_LOCATION]: true,
[fmgPageValues.SCHEDULE]: true,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.QUOTE);
});
});
test("user has capability questions and molding questions > should return capability questions", async () => {
// Arrange
const toRoute = {
query: {},
};
describe("Explicit Routing", () => {
test("Returns the value passed in in nominal case", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: "testValue",
},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: true,
[fmgPageValues.MOLDING_QUESTIONS]: true,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: false,
[fmgPageValues.VIN_LOOKUP]: false,
// Assert
expect(result).toBe("testValue");
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
describe("Verified users", () => {
test("Replace navigation to `vehicle` with `vehicle-damage`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.VEHICLE,
},
};
//Assert
expect(result).toBe(fmgPageValues.CAPABILITY_QUESTIONS);
});
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
test("user has molding questions and part questions > should return molding questions", async () => {
// Arrange
const toRoute = {
query: {},
};
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Assert
expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
});
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: true,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
describe("Vin-Exempt", () => {
test("Replace navigation to `quote` with `estimate`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.QUOTE,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_IS_REPAIR, true);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
});
test("Replace navigation to `insurance-company` with `estimate`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.INSURANCE_COMPANY,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_IS_REPAIR, true);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.ESTIMATE);
});
});
describe("Vin-Required", () => {
test("Replace navigation to `quote` with `vin-lookup`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.QUOTE,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [
{ glassLocation: glassLocations.WINDSHIELD },
]);
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.VIN_LOOKUP);
});
test("Replace navigation to `insurance-company` with `vin-lookup`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.INSURANCE_COMPANY,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, true);
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [
{ glassLocation: glassLocations.WINDSHIELD },
]);
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.VIN_LOOKUP);
});
});
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
describe("Non-verified users", () => {
test("Don't replace navigation to `vehicle` with `vehicle-damage`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.VEHICLE,
},
};
//Assert
expect(result).toBe(fmgPageValues.MOLDING_QUESTIONS);
});
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
test("user has vehicle parts questions > should return vehicle-parts", async () => {
// Arrange
const toRoute = {
query: {},
};
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Assert
expect(result).toBe(fmgPageValues.VEHICLE);
});
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: true,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
test("Don't replace navigation to `quote` with `estimate` or `vin-lookup`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.QUOTE,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.QUOTE);
});
test("Don't replace navigation to `insurance-company` with `estimate` or `vin-lookup`", async () => {
// Arrange
const toRoute = {
query: {
fmgPage: fmgPageValues.INSURANCE_COMPANY,
},
};
store.commit(storeMutations.UPDATE_IS_VERIFIED_INSURANCE, false);
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
// Assert
expect(result).toBe(fmgPageValues.INSURANCE_COMPANY);
});
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.VEHICLE_PARTS);
});
test("user has part questions > should return part-questions", async () => {
// Arrange
const toRoute = {
query: {},
};
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
store.commit(storeMutations.UPDATE_MAKE, "acura");
// Mock out the lazy load calls for all components.
mockLazyLoadComponentReturnValues({
[fmgPageValues.VEHICLE]: true,
[fmgPageValues.VEHICLE_DAMAGE]: true,
[fmgPageValues.ESTIMATE]: true,
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
[fmgPageValues.MOLDING_QUESTIONS]: false,
[fmgPageValues.VEHICLE_PARTS]: false,
[fmgPageValues.PART_QUESTIONS]: true,
[fmgPageValues.VIN_LOOKUP]: false,
});
// Act
const result = await getPageToRouteExistingOrderTo(toRoute);
//Assert
expect(result).toBe(fmgPageValues.PART_QUESTIONS);
});
});

View file

@ -166,6 +166,7 @@ async function saveSessionHelper(
customerPortalLoginToken: savedSessionInfo.data.customerPortalLoginToken,
lockToken: savedSessionInfo.data.lockToken,
settledTenderAmount: savedSessionInfo.data.settledTenderAmount,
billToAccountNumber: savedSessionInfo.data.billToAccountNumber,
},
false
);

View file

@ -80,7 +80,6 @@ export default {
return {
parentAccountNumber: this.parentAccountNumberFromStore(),
originalList: [],
}
},
methods: {
parentAccountNumberFromStore() {
@ -93,8 +92,11 @@ export default {
// Go back to Quote page
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
},
forwardButtonAction() {
this.dispatchStoreAction(
async forwardButtonAction() {
// await any pending save-sessions. if you don't, we will save the new parent account into vuex and then a pending async save-session response overwrites it
await store.getters.applicationUser.saveSessionPromise;
await this.dispatchStoreAction(
this.storeActions.SAVE_PARENT_ACCOUNT_NUMBER,
this.parentAccountNumber,
false

View file

@ -26,6 +26,7 @@
:availableVaps="availableVaps"
:allowItemRemoval="true"
v-model="lineItems"
pageName="payment-method"
servicePackageOptionsCmsName="ServicePackageTitle"
recyclingModalCmsWidgetName="RecycleModal" />

View file

@ -29,6 +29,8 @@
<script>
import { splitCopyOnCMSPlaceHolder } from "@/helpers/cms-content-helper";
import baseMixin from "@/mixins/base-mixin.js";
import { getPromosThatMatchLineItemsOnOrder } from "@/helpers/promotions-helper";
import { getArrayOfAllLineItems } from "@/store";
const INLINE_IMAGE_TOKEN = "custom:inlineImage";
const AFTERPAY_PRICE_TOKEN = "custom:afterpayPrice";
@ -50,7 +52,9 @@ export default {
name: "afterpay-modal-banner",
props: {
cmsWidgetName: String,
availableLineItems: Array,
lineItems: Array,
activePromos: null,
},
data() {
return {};
@ -62,7 +66,7 @@ export default {
},
computed: {
nullSafeLineItems() {
return this.lineItems ?? [];
return this.availableLineItems ?? [];
},
imageUrl() {
return this.getCmsContent(this.cmsWidgetName, "Image");
@ -77,10 +81,23 @@ export default {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
afterpayPrice() {
const price = baseMixin.methods.getTierOnePackagePrice(
let price = baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(this.nullSafeLineItems)
);
let vapsLineItemsForSelectedPackage = this.lineItems.vaps;
vapsLineItemsForSelectedPackage.forEach((lineItem) => {
price += baseMixin.methods.getTotalLineItemPrice(lineItem);
});
if (this.activePromos) {
let allLineItems = getArrayOfAllLineItems(this.lineItems);
const promos = getPromosThatMatchLineItemsOnOrder(this.activePromos, allLineItems);
promos.forEach((promo) => {
price += baseMixin.methods.getTotalLineItemPrice(promo);
});
}
const adjusted = (price / 4).toFixed(2);
return `$${adjusted}`;

View file

@ -37,7 +37,9 @@
<afterpayModalBanner
v-if="!isInsuranceSelected"
cmsWidgetName="AfterpayModalWidget"
:lineItems="availableLineItems" />
:activePromos="lineItems.promos"
:availableLineItems="availableLineItems"
:lineItems="lineItems" />
<promoModalQuestion
class="small mt-4"

View file

@ -458,6 +458,12 @@ export default {
},
updateAndSaveSupportingItems() {
const supportingItems = store.getters.lineItems.supportingItems;
// for insurance orders, fees can get removed causing supportingitems to be null or empty
if (!supportingItems) {
return;
}
// if we have a mobile fee, then save/update supporting items
if (this.selectedAppointmentType == "Mobile") {
const mobileFeeIndex = supportingItems.findIndex(

View file

@ -105,6 +105,12 @@ const routes = [
const pageToRedirectTo = await getPageToRouteExistingOrderTo(to);
// This logic may determine that the user should be sent to heritage -- if so, do that here.
if (pageToRedirectTo === fmgPageValues.HERITAGE) {
await navigateToHeritageFunnel({ shouldSaveSession: false });
return next(false);
}
// Assign our fmgPage so it will load normally like the other pages.
to.query.fmgPage = pageToRedirectTo;
}

View file

@ -1069,6 +1069,7 @@ export const actions = {
customerPortalLoginToken,
lockToken,
settledTenderAmount,
billToAccountNumber,
}
) {
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
@ -1084,6 +1085,7 @@ export const actions = {
context.commit(storeMutations.LOCK_TOKEN, lockToken);
context.commit(storeMutations.Customer_Portal_Login_Token, customerPortalLoginToken);
context.commit(storeMutations.UPDATE_SETTLED_TENDER_AMOUNT, settledTenderAmount);
context.commit(storeMutations.UPDATE_BILL_TO_ACCT_NUMBER, billToAccountNumber);
},
logPageView(
@ -2827,7 +2829,7 @@ export function mapTaxedLineItemsToStoreFormat(availableLineItems, storeLineItem
return lineItems;
}
function getArrayOfAllLineItems(lineItems) {
export function getArrayOfAllLineItems(lineItems) {
let consolidatedLineItemsArray = [];
if (lineItems.glassParts != null)