Merge branch 'develop' into feature/CSR-1382-loose-ends
This commit is contained in:
commit
059195b31d
37 changed files with 794 additions and 426 deletions
|
|
@ -27,9 +27,9 @@ module.exports = {
|
||||||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
statements: 77,
|
statements: 75,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit
|
// Uncomment this to avoid the massive amount of warnings we are getting for onSubmit and onInvalidSubmit
|
||||||
silent: true,
|
//silent: true,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,14 @@ const endpoints = {
|
||||||
url: "/price/api/v1/price/order-items",
|
url: "/price/api/v1/price/order-items",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
|
ValidatePromo: {
|
||||||
|
url: "/price/api/v1/price/order-promo",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
|
RevalidatePromos: {
|
||||||
|
url: "/price/api/v1/price/revalidated-order-promos",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
TaxOrderItems: {
|
TaxOrderItems: {
|
||||||
url: "/price/api/v1/price/taxed-order-items",
|
url: "/price/api/v1/price/taxed-order-items",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|
|
||||||
6
src/constants/service-type.js
Normal file
6
src/constants/service-type.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
const serviceType = {
|
||||||
|
REPLACEMENT: "replacement",
|
||||||
|
REPAIR: "repair",
|
||||||
|
REPLACEMENT_AND_RECALIBRATION: "replacement and recalibration",
|
||||||
|
};
|
||||||
|
export { serviceType };
|
||||||
|
|
@ -51,6 +51,8 @@ const storeActions = {
|
||||||
CLEAR_VIN: "clearVin",
|
CLEAR_VIN: "clearVin",
|
||||||
RESET_SAVE_SESSION_PROMISE: "resetSaveSessionPromise",
|
RESET_SAVE_SESSION_PROMISE: "resetSaveSessionPromise",
|
||||||
GET_SIGNATURE: "getSignature",
|
GET_SIGNATURE: "getSignature",
|
||||||
|
VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA: "validateOrderPromoAndSaveServerData",
|
||||||
|
REVALIDATE_ORDER_PROMOS_AND_UPDATE_STORE: "revalidateOrderPromosAndUpdateStore",
|
||||||
|
|
||||||
// DEPENDENCY MUTATIONS
|
// DEPENDENCY MUTATIONS
|
||||||
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
||||||
|
|
@ -84,6 +86,7 @@ const storeActions = {
|
||||||
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
|
SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING:
|
||||||
"saveSupportingItemsSuppressingStateResetting",
|
"saveSupportingItemsSuppressingStateResetting",
|
||||||
SAVE_VAPS: "saveVaps",
|
SAVE_VAPS: "saveVaps",
|
||||||
|
SAVE_PROMOS: "savePromos",
|
||||||
SAVE_CUSTOMER_DETAILS: "saveCustomerDetails",
|
SAVE_CUSTOMER_DETAILS: "saveCustomerDetails",
|
||||||
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
|
SAVE_SERVICE_LOCATION_TECH_NOTES: "saveServiceLocationTechNotes",
|
||||||
SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag",
|
SAVE_WORK_ORDER_FLAG: "saveWorkOrderFlag",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ const storeMutations = {
|
||||||
UPDATE_VAPS: "updateVaps",
|
UPDATE_VAPS: "updateVaps",
|
||||||
UPDATE_SUPPORTING_ITEMS: "updateSupportingItems",
|
UPDATE_SUPPORTING_ITEMS: "updateSupportingItems",
|
||||||
UPDATE_LINE_ITEMS_SERVER_DATA: "updateLineItemsServerData",
|
UPDATE_LINE_ITEMS_SERVER_DATA: "updateLineItemsServerData",
|
||||||
|
UPDATE_INACTIVE_PROMOS: "updateInactivePromos",
|
||||||
|
UPDATE_PROMOS: "updatePromos",
|
||||||
|
|
||||||
UPDATE_REGISTRATION: "updateRegistration",
|
UPDATE_REGISTRATION: "updateRegistration",
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
@mousedown="closeModal"
|
@mousedown="closeModal"
|
||||||
aria-label="Close"></button>
|
aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body ps-5 pe-5 pb-4 pt-0">
|
<div class="modal-body ps-5 pe-5 pb-4 pt-1">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer px-5 py-4">
|
<div class="modal-footer px-5 py-4">
|
||||||
|
|
@ -179,7 +179,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
overflow: scroll;
|
overflow: auto;
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,17 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
:displayGenericVehicleImage="false" />
|
:displayGenericVehicleImage="false" />
|
||||||
|
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6 col-xl-4">
|
||||||
<alert
|
<alert
|
||||||
ref="alertFewMoreQuestions"
|
ref="alertFewMoreQuestions"
|
||||||
cmsWidgetName="alertWidget"
|
cmsWidgetName="alertWidget"
|
||||||
|
|
@ -25,7 +28,7 @@
|
||||||
v-bind:isDismissible="false" />
|
v-bind:isDismissible="false" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
|
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
|
||||||
<questionChain
|
<questionChain
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,6 @@ export function getDateDifferenceInDays(startDate, endDate) {
|
||||||
// Convert milliseconds to days and return the result
|
// Convert milliseconds to days and return the result
|
||||||
return difference / (1000 * 3600 * 24);
|
return difference / (1000 * 3600 * 24);
|
||||||
}
|
}
|
||||||
export function getFullDayName(date) {
|
|
||||||
// Use a ternary operator to check if the input is a valid date object
|
|
||||||
return date instanceof Date
|
|
||||||
? // Use the built-in method toLocaleDateString() to get the full day name in the current locale
|
|
||||||
date.toLocaleDateString(undefined, { weekday: "long" })
|
|
||||||
: // Return undefined if the input is not a valid date object
|
|
||||||
undefined;
|
|
||||||
}
|
|
||||||
export function getFullMonthName(date) {
|
|
||||||
// Use a ternary operator to check if the input is a valid date object
|
|
||||||
return date instanceof Date
|
|
||||||
? // Use the built-in method toLocaleDateString() to get the full month name in the current locale
|
|
||||||
date.toLocaleDateString(undefined, { month: "long" })
|
|
||||||
: // Return undefined if the input is not a valid date object
|
|
||||||
undefined;
|
|
||||||
}
|
|
||||||
export function get12HourTimeFormat(time) {
|
export function get12HourTimeFormat(time) {
|
||||||
// Check correct time format and split into components
|
// Check correct time format and split into components
|
||||||
time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time];
|
time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time];
|
||||||
|
|
@ -112,7 +96,7 @@ export function shortTimeString(date) {
|
||||||
// Use a ternary operator to check if the input is a valid date object
|
// Use a ternary operator to check if the input is a valid date object
|
||||||
return date instanceof Date
|
return date instanceof Date
|
||||||
? // Use the built-in method toLocaleTimeString() to get the short time string in the current locale
|
? // Use the built-in method toLocaleTimeString() to get the short time string in the current locale
|
||||||
date.toLocaleTimeString(undefined, { hour: "numeric", minute: "numeric", hour12: true })
|
date.toLocaleTimeString("en-us", { hour: "numeric", minute: "numeric", hour12: true })
|
||||||
: // Return undefined if the input is not a valid date object
|
: // Return undefined if the input is not a valid date object
|
||||||
undefined;
|
undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import {
|
import {
|
||||||
getFullDayName,
|
|
||||||
getFullMonthName,
|
|
||||||
get12HourTimeFormat,
|
get12HourTimeFormat,
|
||||||
get12HourTimeMobileFormat,
|
get12HourTimeMobileFormat,
|
||||||
getDateFormat,
|
getDateFormat,
|
||||||
|
|
@ -52,33 +50,17 @@ describe("date-helper.js", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// it("getFullMonthName should return full month format.", () => {
|
it("getDateFormat should return date in the given format.", () => {
|
||||||
// // Arrange / Act
|
// Arrange / Act
|
||||||
// const date = new Date("2023-10-01");
|
const dateString = "2023-10-01";
|
||||||
// const monthName = getFullMonthName(date);
|
const dateParts = dateString.split("-");
|
||||||
|
const date = new Date(dateParts[0], parseInt(dateParts[1]) - 1, dateParts[2]);
|
||||||
|
const format = "yyyy-MM-dd";
|
||||||
|
const formattedDate = getDateFormat(date, format);
|
||||||
|
|
||||||
// // Assert
|
// Assert
|
||||||
// expect(monthName).toEqual("October");
|
expect(formattedDate).toEqual("2023-10-01");
|
||||||
// });
|
});
|
||||||
|
|
||||||
// it("getFullDayName should return full Day Name format.", () => {
|
|
||||||
// // Arrange / Act
|
|
||||||
// const date = new Date("2023-10-01");
|
|
||||||
// const dayName = getFullDayName(date);
|
|
||||||
|
|
||||||
// // Assert
|
|
||||||
// expect(dayName).toEqual("Sunday");
|
|
||||||
// });
|
|
||||||
|
|
||||||
// it("getDateFormat should return date in the given format.", () => {
|
|
||||||
// // Arrange / Act
|
|
||||||
// const date = new Date("2023-10-01");
|
|
||||||
// const format = "yyyy-MM-dd";
|
|
||||||
// const formattedDate = getDateFormat(date, format);
|
|
||||||
|
|
||||||
// // Assert
|
|
||||||
// expect(formattedDate).toEqual("2023-10-01");
|
|
||||||
// });
|
|
||||||
|
|
||||||
it("should return the correct difference in days", function () {
|
it("should return the correct difference in days", function () {
|
||||||
// Define some sample dates and their expected differences
|
// Define some sample dates and their expected differences
|
||||||
|
|
|
||||||
101
src/helpers/promotions-helper.js
Normal file
101
src/helpers/promotions-helper.js
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import store from "@/store";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
|
||||||
|
export const promoPartNumberStrings = {
|
||||||
|
WIPER_DISCOUNT_PART_NUMBER: "WIPER DISCOUNT",
|
||||||
|
RAIN_DEFENSE_DISCOUNT_PART_NUMBER: "DISC RAIN DEFEN",
|
||||||
|
GLASS_DISCOUNT_PART_NUMBER: "DISCOUNT",
|
||||||
|
GLASS_CLEANER_DISCOUNT_PART_NUMBER: "DISC GLASS CLN",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pagesToStripPromoQueryStringFrom = ["quote", "payment-method"];
|
||||||
|
|
||||||
|
export function getAddableVapsFromAvailableLineItems(availableLineItems) {
|
||||||
|
const addableVaps = [];
|
||||||
|
addableVaps.push(...findLineItemsWithPartType(partTypeStrings.FRONT_WIPER, availableLineItems));
|
||||||
|
addableVaps.push(
|
||||||
|
...findLineItemsWithPartType(partTypeStrings.RAIN_DEFENSE, availableLineItems)
|
||||||
|
);
|
||||||
|
return addableVaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeVapsPromosFromPromoArray(promoArray) {
|
||||||
|
if (!promoArray) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const filteredPromoArray = promoArray.filter((promo) => {
|
||||||
|
return (
|
||||||
|
promo.partNumber != promoPartNumberStrings.WIPER_DISCOUNT_PART_NUMBER &&
|
||||||
|
promo.partNumber != promoPartNumberStrings.RAIN_DEFENSE_DISCOUNT_PART_NUMBER &&
|
||||||
|
promo.partNumber != promoPartNumberStrings.GLASS_CLEANER_DISCOUNT_PART_NUMBER
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return filteredPromoArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function revalidatePromosAndValidateNewPromo(
|
||||||
|
newPromo,
|
||||||
|
pricedLineItems,
|
||||||
|
pageNameToLog
|
||||||
|
) {
|
||||||
|
const hasActivePromos = store.getters.order.lineItems.promos;
|
||||||
|
const hasHasInactivePromos = store.getters.order.lineItems.inactivePromos;
|
||||||
|
const hasNewPromo = !!newPromo;
|
||||||
|
const addableVaps = getAddableVapsFromAvailableLineItems(pricedLineItems);
|
||||||
|
|
||||||
|
let validatePromoResponse = null;
|
||||||
|
let revalidatePromoResponse = null;
|
||||||
|
|
||||||
|
if (hasActivePromos || hasHasInactivePromos) {
|
||||||
|
revalidatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.REVALIDATE_ORDER_PROMOS_AND_UPDATE_STORE,
|
||||||
|
null,
|
||||||
|
pageNameToLog,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (hasNewPromo) {
|
||||||
|
validatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA,
|
||||||
|
{
|
||||||
|
promoCode: newPromo,
|
||||||
|
addableVaps: addableVaps,
|
||||||
|
},
|
||||||
|
pageNameToLog,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { validatePromoResponse, revalidatePromoResponse };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPromosThatMatchLineItemsOnOrder(promos, lineItemsOnOrder) {
|
||||||
|
const matchingPromos = [];
|
||||||
|
promos.forEach((promo) => {
|
||||||
|
let allIdsMatch = true;
|
||||||
|
promo.discountedLineItemIds.forEach((id) => {
|
||||||
|
if (lineItemsOnOrder.filter((x) => x.id === id).length === 0) {
|
||||||
|
allIdsMatch = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (allIdsMatch) {
|
||||||
|
matchingPromos.push(promo);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return matchingPromos;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldStripPromoQueryString(fmgPageQueryValue) {
|
||||||
|
return pagesToStripPromoQueryStringFrom.includes(fmgPageQueryValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
function findLineItemsWithPartType(typeToFind, itemsToSearch) {
|
||||||
|
const partTypeMatches = itemsToSearch?.filter(
|
||||||
|
(lineItem) => lineItem.partType.toUpperCase() === typeToFind.toUpperCase()
|
||||||
|
);
|
||||||
|
return partTypeMatches;
|
||||||
|
}
|
||||||
|
|
@ -77,9 +77,9 @@ describe("Add-to-calendar methods...", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled();
|
expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled();
|
||||||
});
|
});
|
||||||
test("serviceType should return 'replacement and recalibration' when isRepair and funnelHasRecalibrationPart true.", () => {
|
test("serviceType should return 'replacement and recalibration' when isRepair is false and funnelHasRecalibrationPart true.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.damage.isRepair = true;
|
store.getters.order.damage.isRepair = false;
|
||||||
store.getters.funnelHasRecalibrationPart = true;
|
store.getters.funnelHasRecalibrationPart = true;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -89,9 +89,9 @@ describe("Add-to-calendar methods...", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(testValue).toEqual("replacement and recalibration");
|
expect(testValue).toEqual("replacement and recalibration");
|
||||||
});
|
});
|
||||||
test("serviceType should return 'replacement' when isRepair is true and funnelHasRecalibrationPart is false.", () => {
|
test("serviceType should return 'replacement' when isRepair is false and funnelHasRecalibrationPart is false.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.damage.isRepair = true;
|
store.getters.order.damage.isRepair = false;
|
||||||
store.getters.funnelHasRecalibrationPart = false;
|
store.getters.funnelHasRecalibrationPart = false;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -101,9 +101,9 @@ describe("Add-to-calendar methods...", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(testValue).toEqual("replacement");
|
expect(testValue).toEqual("replacement");
|
||||||
});
|
});
|
||||||
test("serviceType should return 'repair' when isRepair is false.", () => {
|
test("serviceType should return 'repair' when isRepair is true.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.damage.isRepair = false;
|
store.getters.order.damage.isRepair = true;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -126,7 +126,7 @@ describe("Add-to-calendar methods...", () => {
|
||||||
test("getappointmentData should return expected model value for appointmentType mobile.", () => {
|
test("getappointmentData should return expected model value for appointmentType mobile.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||||
store.getters.order.damage.isRepair = true;
|
store.getters.order.damage.isRepair = false;
|
||||||
store.getters.funnelHasRecalibrationPart = true;
|
store.getters.funnelHasRecalibrationPart = true;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -176,7 +176,7 @@ describe("Add-to-calendar methods...", () => {
|
||||||
test("getappointmentData should return expected model value for appointmentType DROP_OFF.", () => {
|
test("getappointmentData should return expected model value for appointmentType DROP_OFF.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||||
store.getters.order.damage.isRepair = false;
|
store.getters.order.damage.isRepair = true;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -188,7 +188,7 @@ describe("Add-to-calendar methods...", () => {
|
||||||
test("getappointmentData should return expected model value for appointmentType IN_SHOP.", () => {
|
test("getappointmentData should return expected model value for appointmentType IN_SHOP.", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||||
store.getters.order.damage.isRepair = false;
|
store.getters.order.damage.isRepair = true;
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -239,12 +239,6 @@ function setupMocks({ customMountOptions }) {
|
||||||
});
|
});
|
||||||
|
|
||||||
const wrapper = shallowMount(addToCalendar, mountOptions);
|
const wrapper = shallowMount(addToCalendar, mountOptions);
|
||||||
|
|
||||||
//wrapper.vm.setCmsContent = jest.fn();
|
|
||||||
//wrapper.vm.$refs.datePicker.initializeComponent = jest.fn();
|
|
||||||
//wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn();
|
|
||||||
//wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
|
||||||
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn();
|
||||||
|
|
||||||
return { wrapper };
|
return { wrapper };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import {
|
||||||
import { getCalendarFile, download } from "@/helpers/add-to-calendar-helper";
|
import { getCalendarFile, download } from "@/helpers/add-to-calendar-helper";
|
||||||
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { serviceType } from "@/constants/service-type";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "add-to-calendar",
|
name: "add-to-calendar",
|
||||||
|
|
@ -127,14 +128,14 @@ export default {
|
||||||
serviceType() {
|
serviceType() {
|
||||||
const isRepair = store.getters.order.damage.isRepair;
|
const isRepair = store.getters.order.damage.isRepair;
|
||||||
const funnelHasRecalibrationPart = store.getters.funnelHasRecalibrationPart;
|
const funnelHasRecalibrationPart = store.getters.funnelHasRecalibrationPart;
|
||||||
if (isRepair) {
|
if (!isRepair) {
|
||||||
if (funnelHasRecalibrationPart) {
|
if (funnelHasRecalibrationPart) {
|
||||||
return "replacement and recalibration";
|
return serviceType.REPLACEMENT_AND_RECALIBRATION;
|
||||||
} else {
|
} else {
|
||||||
return "replacement";
|
return serviceType.REPLACEMENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "repair";
|
return serviceType.REPAIR;
|
||||||
},
|
},
|
||||||
routeCode() {
|
routeCode() {
|
||||||
return store.getters.order.schedule.routeCode;
|
return store.getters.order.schedule.routeCode;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
<div>
|
<div>
|
||||||
<div class="container-fluid pb-2">
|
<div class="container-fluid pb-2">
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<img :src="ScheduleConfirmationImage" />
|
<img :src="ScheduleConfirmationImage" />
|
||||||
|
|
@ -55,15 +55,11 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config.js";
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
|
import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper";
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
import {
|
import { get12HourTimeFormat, get12HourTimeMobileFormat } from "@/helpers/date-helper";
|
||||||
getFullDayName,
|
|
||||||
getFullMonthName,
|
|
||||||
get12HourTimeFormat,
|
|
||||||
get12HourTimeMobileFormat,
|
|
||||||
} from "@/helpers/date-helper";
|
|
||||||
export default {
|
export default {
|
||||||
name: "confirmation",
|
name: "confirmation",
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -174,10 +170,14 @@ export default {
|
||||||
return `${this.ProviderAddress},<br/> ${this.ProviderCity}, ${this.ProviderState} ${this.ProviderZipCode}`;
|
return `${this.ProviderAddress},<br/> ${this.ProviderCity}, ${this.ProviderState} ${this.ProviderZipCode}`;
|
||||||
},
|
},
|
||||||
ScheduleDateFormatted() {
|
ScheduleDateFormatted() {
|
||||||
const scheduleDate = new Date(this.ScheduleDate);
|
// This conversion ensures we don't get get GMT induced date changes
|
||||||
return `${getFullDayName(scheduleDate)}, ${getFullMonthName(
|
const dateObject = convertDateStringToDate(this.ScheduleDate);
|
||||||
scheduleDate
|
// Ex: Tuesday, April 22
|
||||||
)} ${scheduleDate.getDate()}`;
|
return dateObject.toLocaleDateString("en-us", {
|
||||||
|
weekday: "long",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
},
|
},
|
||||||
ScheduleTimeFormatted() {
|
ScheduleTimeFormatted() {
|
||||||
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
|
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="my-5" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="my-5" />
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" />
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" />
|
||||||
<funnelSubHeader class="pb-4" cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader class="pb-4" cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
|
@ -33,12 +33,13 @@
|
||||||
|
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<alert
|
<alert
|
||||||
class="my-4"
|
class="mt-4 mb-4"
|
||||||
:cmsWidgetName="alertInfo"
|
:cmsWidgetName="alertInfo"
|
||||||
alertClass="alert-info"
|
alertClass="alert-info"
|
||||||
v-bind:isDismissible="false" />
|
v-bind:isDismissible="false" />
|
||||||
|
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
class="mb-4"
|
||||||
cmsWidgetName="ServiceZipQuestionWidget"
|
cmsWidgetName="ServiceZipQuestionWidget"
|
||||||
v-model="serviceZipCode"
|
v-model="serviceZipCode"
|
||||||
inputId="serviceZipCode"
|
inputId="serviceZipCode"
|
||||||
|
|
@ -47,6 +48,7 @@
|
||||||
validationRules="zip-required|zip-format" />
|
validationRules="zip-required|zip-format" />
|
||||||
|
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
class="mb-0"
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="emailAddress"
|
v-model="emailAddress"
|
||||||
inputId="emailAddress"
|
inputId="emailAddress"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
@ -38,45 +38,6 @@
|
||||||
vapsTilesCmsName="VapsProductTiles"
|
vapsTilesCmsName="VapsProductTiles"
|
||||||
v-on="{ 'buttonEvent.openModal': openModal }" />
|
v-on="{ 'buttonEvent.openModal': openModal }" />
|
||||||
|
|
||||||
<div>
|
|
||||||
<button v-on:click="ccdPayment">
|
|
||||||
<img
|
|
||||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAcCAYAAAAjmez3AAAAAXNSR0IArs4c6QAABc5JREFUWAndV2lsVFUU/t42e6cznWmB0mnpwiKgpagsNRIQikECUWM0ERSXHyyaCOGX/BCixkiMBhOwStBEraZG4g8DyCagokLLolithXaYtsPQ6cLMtNPpbO+N575hGohpmZElradJ33v3nnvu+c75zrl3OKTk8V1F0OsKoJU1qaER+5QSPLqjHdAZ2vHFyl7mJ4ePTkl8u2fNotLcTcVWwxiNwMdGLICrjvH07ArFNCc8gcPO857VqF7hBF76alXVp/WJUExJjDY52h5MTH//8K94cVeOmF9geaGq2IqfnF2IyMpIT8agfxzHwSDxsBkNcyReXyjGFKXk3Z9b4PWFiGjcoOJoebFlaSOmcWaHSMgUkScAIjFvFAKhgJPzisLq5n8hIkNBZc7+qdhGGyrmOhOR0aoiPxt+qxGMYcMJYx5TuboWKSNsTWqOtQumk9JL6TDbPCkl6O9fPYXmmH5KUmtS30M92Z7d/dGEqzuSELN1IqqX3wNHtn4o/cHxmJyAJFy75eDUDV+Yc75wFCaNCDqrbqifrsLGfQ1478JlqNRKJwIKUa/O7cOOEy6KQgSTck3YtnQ69QgOjV1BbNjboDq4bek0HHX2oPZsG5ZOy8f6yhJ8WNeK2nNu9AzEkCUJmF9qx7rZE1BgTgbviLMbW4+dV1M9dVw23lg0WQWcDpgUO9IODU8OzyQKzhhnxqEGD/b87SVqJRAngFuONOHAb27kmrTI1kmornPhcKMXuQaNqvfyt+dwwu2Hhmw0U5uvOevGpUBY9TMQjmHrj8042NSJg40d2NvkRV9ETgfDdTpqRq4bGeZDT9GcV2wDtCIG4jLRjMduArXvzw7kUIY2PlACH0X9d08A1hwj5jis+ICyIUdiWFSWj/3PzUHLlX64CMxsmmOy73wSwFMzHfjmrw5E4grCZDtTSTsjKcMmjQCjXqLNFDjJqXeOtyBI3F8zawKm5mWh9o9LiEdlPDwxFw6LHhNtBqpyHsdauvDM12dg1kpYWJqrmvMNRLH5+yYYKIur7y+iORF+ylAodgeAGKlYbXqNSqnPiCJ1riuYnG/BBqoFmWj2XVOX2soXUh2wol5V4cAr88rUWqqpb8Wyz0+iPTCgAtlO9Xbhci+WTBmDBSV2GCnjQcpeOJb5VSnzjNBmNoOEUDiO7Sdd4MnZN6k47UYNTnv8VPh9sFr1uJfAMWF0ZE1h98pZKB1jxsmLV/DluUtoIzC7TrWxnoz+aBxv/9CM/lgcCoHoJTCZSkY1wowbKf0WogI7DHqI608Qtx+ZlKfue6i5G77eMCrL7JhkN+LVg41wU1E/O3M84tS61Usphc5OgWAF30agdQYtdUM/fmnzEaAkpTqpK2YqGQPR053MSjVCnsFEZ8/6ymIYiG5RAna8tUcdryy0qjXE2mrdxR7UUBdThdY+WV6AKfYsAlkPjrJVvexuzHJYEKWaW03drY5qqaPvvwLh0mcYuz6/9tBdWFFRhDyi09zCJIVANjbNn4J1c8tQMdZM9NNg99OzcYbo1uYfUFt1kcWIKmoCrOXufKyCruECqkptg6f6juUz4KGMTs0zZZCQ5AEtcohD6G+CAj5550rDRLlJQLmZDNA5IgfciNNTIIAP2ikg7N4gexH3K3BQ/TgK6buAji12cvEhJEJeUB/Do4WCOiYHLqsg2bb3mWnMwtZ3quvZ2HAiaHgIMWKBVgtRo/TB6KpBkH4C007DrbuJOXJOldQ5fBOmrllqEGIwBhdz0M3gkjXCUSTU19sF5Jrdb+UrxwKTDNIo83zoKFztWozDmR9CQ5u9QzOqz0m6igonxWWdA5JkpN1HV4JESUaUN/OIc1GxM6g9sEdYu3ZVRT4BubXFeLvzwi6Xp8/Wh3CxuVnsaO1+a/P+hnKOEyoXT7Cod6KRDoe1eifdKuiuphxp6Xod1gF3suSf/8SRM3n85rF6aUlCSWjoLBjpWPionGht8fZ9DEP2TmxZEP8H777k8QqWrnUAAAAASUVORK5CYII="
|
|
||||||
alt="We accept Visa" />
|
|
||||||
</button>
|
|
||||||
<button v-on:click="ccdPayment">
|
|
||||||
<img
|
|
||||||
src="data:image/png;base64,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"
|
|
||||||
alt="Mastercard" />
|
|
||||||
</button>
|
|
||||||
<button v-on:click="ccdPayment">
|
|
||||||
<img
|
|
||||||
src="data:image/png;base64,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"
|
|
||||||
alt="American Express" />
|
|
||||||
</button>
|
|
||||||
<button v-on:click="ccdPayment">
|
|
||||||
<img
|
|
||||||
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAcCAYAAADMW4fJAAAAAXNSR0IArs4c6QAABzFJREFUWAntWHtQnNUV/30P9gXLJkvCsjxMDJKFQEhIY4MghPicSZppp0k1YrSjfzRxJmrHmT6i7YxjHyoznalmDK2vPMyIxqnptDVV6yNgiAQTUggYwIUQhbDRhcAuj+V73Z77bR6Lgg9KLJnpnfn2u/d+955zfvf8zr3nroBzhT0MMdiKFIsTaVIiZmnnP8zQt8xgqMMIjobQk1qFIDdT4D/sXljDnszVlozvPs7c87KEOLvC+2dyEUBoImEBwc46pePtn7keDR0WyCOOCLKWI/fmvbY1D3tgT5rJGMbbxgywPr8e+cevG/HB3h+Io7jKbSSlrZIyi+deVkA4LEGE4EyV5MzSK3TvgqtF1ZFgh8UxT0r0iuNhXyYtUYLo8lphdaVdngAmWef/g5lkYf7n3ZfMM4yxGHAMTBkFGxmgcyC2P2bIN6iOl31xonyxerHGBweDQfxt3z6MRMaQnpaGgmUFSKV3U2MT+oKfobCoCNXvvovm5hbk5y/GdTfcAFmWUV9/GIfeq4Wiqli7di3yc33QTrwBrenvMAZ7IWdfD9X3PRxsPgmbzYayVWXQdR2dH/lxpOEochYtQs2BA9A1HUlz5yA3NxfHjx9HaHAQWQsXoqBgGYZHhnHl/PkQJemi0VSbFEzg9Gk89+zzKPhOAVqamrBzxw78ZNMmnOzqwpHDdQicOYOdzz2PdT9aj97eAAb6z6K2thaVldtRUlKKxEQnmhvqkRl4A0JdJamic1iQoFb/G0rLv/CxWorXqutMY61WC/a+shc93T3QVA0v7NqNDeXlsFltaD9xArt37UIxLd7+1/aj8JpCbLnvPgiCed5/NRg+QhBFJKd4cMedd5orsu2PT2DPnj3khXzY7Q4MDgxC0TRkZ+dgBSnoC/ah6sUXsXnzZtyyYYNJp0jwFLTd5STMAESrqVggQNaQHytss/CW3Y5jxxqQnp6BY0cbcO9P78fZgQF4vV4Ul1yLJLcb7W3tmDtnDtbfeitC4SF0dnZiDrUnKhN6JnagYRhwOp1Eq2tQe/AgwqEQRKLTD9evQ5hcX/HY41iUtwir16xBJDJKNMm9MN3CVOgCZXmxq0gLKlIqkpYAFBUVY99fXkX+knzEk468xYtRfaAa7R+143eP/IZk5WDp0qVE+T78/pHfwmq34vaNGy/I/3zlKzcAkQwZGhpC3ft18KZ6kehygRFAF71/+auH8GjFY2hvbcWhQ+/DYbOjra01qoPm6ZKVxhKvY4Oe4p/Rye1MX0iUKUJjYyNerqpCWVkZ4uPjYeiaCWrb9qew9cEH4U5KgiVOxiz3bGKEHUXFxZ/HcKE9sWdIuU4G957uwQ6KC53aZ/v7sOmee9DW2kb1fvypshJtxGdPSgoFsIGVRIucbB+eenIbGogyHGy6x43v56wF6v8MqBGAApYRNYXkPMSt3IIsVYLPtxCBQAA33nSTCZpTt4M2gyqidMa8eaahfKMoL78NTz/9DP5QUYFfEEgOzCym16PxI229MSVJtDlL5AXFBYJ7/oUBcZKMxFkueDwe09U/vvtuosMSWCkofTnZWEUrySmoKCo23rER15aWUn8OfARogHjPvxWVlOGKFauBpCySSwolF+Rl5bBc9wAkd7q5+6VnZJieWH71cjNOueHu2W5TT3xCAnJIZpbPhxWFhcjNyyOKCsigOQ6HI2qrpsDoblAUf807wmDF0izZlbrVdv3P7xIzV0YHTPsvnTNjI+YjOueauKZDBRsdgNb6OvSa7WGl/+RDE9NsOjSNkyFAsMabz7juqTa0MWid70H7YA/0jgPkLSpS3MTnzFR1XPJ5BELvqIHa8BK9D1IcDhEIS9TTtLF8S575b2ASRelurH9yBFr9TvONyCDt73FRIDGiZy4YfosMn4Hmp9Tmw39C7yRP0Lllrj9t+ROVmQeGqGSc+RDKsVfAuupgBP2gvy4Ig43o9OXmfvnXieBfij4OoJ9Sn65D0Fv2w+g9TqncMGnSCQDFhDw+oZzMhCgYFptvTDZ0mvt1FcbpJqj+GrCeo9ADJ4BQgOKA9ibuAboOU+PrK2VMkK2Uf+vQR5gaoWwwust9fQnfYKShgUXC9G9KB9S2t2F8fBjs7CcUFwRAIxrFcQ/QM4VCx7Eh0h9PsjXUPzgsxh1hgRYD2TebW/YU5H1xCtPB6P5ihOj5zA/9FBnffZTuND3EHqIPfQdl5qYXLFNnO09kBFkekwzWLMPfMyAuiX9TaX3rr4LDtV5cUAbYE0nZF+2btIfvPGNDtNKniPtdMPq6qM6fbgLTDYyGz9GHtlPOaA5iGkjAgVhkYTiisJeC0Bt4m9stfHp/cnKC17MFzuR1dOeYHZvo8jGTFuIqSRCYMSYRIAHKqGAoETKZp/4yiaLkkiOg21RUGY09V+WQmMFzaC6BJ2/nV/D8+7xWcyZoIi0b55TARMqFBVHqlg3jWWu6Uinc1RX5D8Lhz9D2zGBFAAAAAElFTkSuQmCC"
|
|
||||||
alt="and Discover" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button v-on:click="paypal">
|
|
||||||
<img
|
|
||||||
src="https://www.paypalobjects.com/webstatic/en_US/i/buttons/PP_logo_h_100x26.png"
|
|
||||||
alt="PayPal" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button v-on:click="afterpay">
|
|
||||||
<img
|
|
||||||
src="https://business.afterpay.com/rs/539-RJA-633/images/AP_logo_badge_6328x2204_blackmint_png.png"
|
|
||||||
width="100"
|
|
||||||
height="26"
|
|
||||||
alt="Afterpay" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<button @click="openModal('RainDefenseModal')">OPEN MODAL</button>
|
|
||||||
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
|
<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
|
||||||
<paymentMethodQuestion
|
<paymentMethodQuestion
|
||||||
v-if="!isPiaDisabled"
|
v-if="!isPiaDisabled"
|
||||||
|
|
@ -291,13 +252,25 @@ export default {
|
||||||
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
switch (this.paymentMethod) {
|
||||||
this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false);
|
case paymentMethods.CREDIT_CARD:
|
||||||
await submitWorkOrder({ pageNameToLog: "payment-method" });
|
this.piaSetup("cc");
|
||||||
this.$router.navigateWithoutSaving(
|
break;
|
||||||
this.navigationScenarios.CLICKED_FORWARD,
|
case paymentMethods.PAYPAL:
|
||||||
this.$route
|
this.piaSetup("pp");
|
||||||
);
|
break;
|
||||||
|
case paymentMethods.AFTERPAY:
|
||||||
|
this.piaSetup("ap");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
||||||
|
this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false);
|
||||||
|
await submitWorkOrder({ pageNameToLog: "payment-method" });
|
||||||
|
this.$router.navigateWithoutSaving(
|
||||||
|
this.navigationScenarios.CLICKED_FORWARD,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async ccdPayment() {
|
async ccdPayment() {
|
||||||
this.piaSetup("cc");
|
this.piaSetup("cc");
|
||||||
|
|
@ -311,16 +284,18 @@ export default {
|
||||||
async piaSetup(payNowType) {
|
async piaSetup(payNowType) {
|
||||||
this.$refs.loadingModal.showModal();
|
this.$refs.loadingModal.showModal();
|
||||||
|
|
||||||
//leaving site, clear any save session promises that we will not be able to resolve when we return
|
// since we're leaving the site for pia, clear any save session promises that we will not be able to resolve when we return
|
||||||
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
await this.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);
|
||||||
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, payNowType, false);
|
await this.dispatchStoreAction(storeActions.SAVE_PAY_NOW_TYPE, payNowType, false);
|
||||||
|
|
||||||
// make sure pia error codes are reset, and get a pia work order in delete status
|
// make sure pia error codes are reset
|
||||||
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false);
|
||||||
this.dispatchStoreAction(storeActions.SAVE_PIA_WORK_ORDER, true);
|
|
||||||
|
|
||||||
|
// if we don't have a work order in delete substatus, set the flag and submit
|
||||||
if (!store.getters.order.workOrderNumber) {
|
if (!store.getters.order.workOrderNumber) {
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_WORK_ORDER, true);
|
||||||
await submitWorkOrder({ pageNameToLog: "payment-method" });
|
await submitWorkOrder({ pageNameToLog: "payment-method" });
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_PIA_WORK_ORDER, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$router.navigateWithoutSaving(
|
this.$router.navigateWithoutSaving(
|
||||||
|
|
@ -352,7 +327,8 @@ export default {
|
||||||
const enablePIA =
|
const enablePIA =
|
||||||
this.getSettingValue(experimentSettings.SUBMIT_ORDER_ENABLE_PIA) === "true";
|
this.getSettingValue(experimentSettings.SUBMIT_ORDER_ENABLE_PIA) === "true";
|
||||||
|
|
||||||
return !(piaExperience && enablePIA);
|
return false;
|
||||||
|
//return !(piaExperience && enablePIA);
|
||||||
},
|
},
|
||||||
paymentMethod() {
|
paymentMethod() {
|
||||||
if (this.isPiaDisabled) {
|
if (this.isPiaDisabled) {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,12 @@ jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||||
navigateToHeritageFunnel: jest.fn(),
|
navigateToHeritageFunnel: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/promotions-helper", () => ({
|
||||||
|
revalidatePromosAndValidateNewPromo: jest.fn(() => {
|
||||||
|
return { validatePromoResponse: null, revalidatePromoResponse: null };
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
"@/store",
|
"@/store",
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -43,11 +49,20 @@ jest.mock("@/mixins/base-mixin", () => ({
|
||||||
getTierOnePackagePrice() {
|
getTierOnePackagePrice() {
|
||||||
return mockTierOnePrice;
|
return mockTierOnePrice;
|
||||||
},
|
},
|
||||||
filterOutFees() {
|
filterOutFees(items) {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
filterOutFees: jest.fn().mockImplementation(() => {
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let mockTierOnePrice = 501;
|
let mockTierOnePrice = 501;
|
||||||
|
|
||||||
const mockPriceOrderStoreAction = storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA;
|
const mockPriceOrderStoreAction = storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA;
|
||||||
|
|
@ -93,6 +108,16 @@ afterEach(() => {
|
||||||
describe("quote.vue", () => {
|
describe("quote.vue", () => {
|
||||||
test("IsInsurance false should navigateWithSaving", async () => {
|
test("IsInsurance false should navigateWithSaving", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
|
store.getters.payment = {
|
||||||
|
insuranceCoverage: {},
|
||||||
|
isInsurance: false,
|
||||||
|
};
|
||||||
|
store.getters.order = {
|
||||||
|
lineItems: [],
|
||||||
|
payment: {
|
||||||
|
parentAccountNumber: 167132,
|
||||||
|
},
|
||||||
|
};
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
customMountOptions: {
|
customMountOptions: {
|
||||||
router: {
|
router: {
|
||||||
|
|
@ -102,11 +127,6 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
store.getters.payment = {
|
|
||||||
insuranceCoverage: {},
|
|
||||||
isInsurance: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
wrapper.vm.dispatchStoreAction = jest.fn(() => {
|
wrapper.vm.dispatchStoreAction = jest.fn(() => {
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
|
|
@ -124,20 +144,28 @@ describe("quote.vue", () => {
|
||||||
|
|
||||||
test("IsInsurance true should navigateToHeritageFunnel", async () => {
|
test("IsInsurance true should navigateToHeritageFunnel", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
|
store.getters = {
|
||||||
|
payment: {
|
||||||
|
insuranceCoverage: {},
|
||||||
|
isInsurance: true,
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
lineItems: [],
|
||||||
|
payment: {
|
||||||
|
parentAccountNumber: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
customMountOptions: {
|
customMountOptions: {
|
||||||
router: {
|
router: {
|
||||||
navigateWithSaving: jest.fn(),
|
navigateWithSaving: jest.fn(),
|
||||||
},
|
},
|
||||||
route: { quote },
|
route: { quote },
|
||||||
|
mixins: [mockMixin],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
store.getters.payment = {
|
|
||||||
insuranceCoverage: {},
|
|
||||||
isInsurance: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
wrapper.vm.dispatchStoreAction = jest.fn(() => {
|
wrapper.vm.dispatchStoreAction = jest.fn(() => {
|
||||||
return {
|
return {
|
||||||
data: [],
|
data: [],
|
||||||
|
|
@ -154,9 +182,9 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should pass arePagePrerequisitesValid with a repair order", () => {
|
test("should pass arePagePrerequisitesValid with a repair order", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
|
lineItems: [],
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
zipCode: "12345",
|
zipCode: "12345",
|
||||||
zipCodeCtu: "value",
|
zipCodeCtu: "value",
|
||||||
|
|
@ -172,6 +200,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
|
@ -179,7 +208,6 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should pass arePagePrerequisitesValid with a replace order", () => {
|
test("should pass arePagePrerequisitesValid with a replace order", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
|
|
@ -200,6 +228,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
|
@ -207,9 +236,9 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should fail arePagePrerequisitesValid without glass parts or flagged as repair", () => {
|
test("should fail arePagePrerequisitesValid without glass parts or flagged as repair", () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
|
lineItems: [],
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
zipCode: "12345",
|
zipCode: "12345",
|
||||||
zipCodeCtu: "value",
|
zipCodeCtu: "value",
|
||||||
|
|
@ -220,6 +249,7 @@ describe("quote.vue", () => {
|
||||||
referralNumber: "1234567",
|
referralNumber: "1234567",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||||
|
|
||||||
|
|
@ -227,8 +257,6 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should have non-null values for necessary data members after 'beforeRouteEnter'", async () => {
|
test("should have non-null values for necessary data members after 'beforeRouteEnter'", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
|
|
@ -236,7 +264,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
//mock this to avoid needing to populate this.$route in an unrelated test
|
//mock this to avoid needing to populate this.$route in an unrelated test
|
||||||
wrapper.vm.getDefaultIsInsuranceSelectedValue = jest.fn();
|
wrapper.vm.getDefaultIsInsuranceSelectedValue = jest.fn();
|
||||||
|
|
||||||
|
|
@ -257,8 +285,6 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should default to insurance if query param 'isInsurance' is true", async () => {
|
test("should default to insurance if query param 'isInsurance' is true", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
|
|
@ -272,6 +298,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
wrapper.vm.$route = { query: { isInsurance: "true" } };
|
wrapper.vm.$route = { query: { isInsurance: "true" } };
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -287,8 +314,6 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should default to cash if query param 'isInsurance' is false", async () => {
|
test("should default to cash if query param 'isInsurance' is false", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
|
|
@ -302,6 +327,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
wrapper.vm.$route = { query: { isInsurance: "false" } };
|
wrapper.vm.$route = { query: { isInsurance: "false" } };
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -317,7 +343,6 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should default to insurance if insurance selection is saved to store", async () => {
|
test("should default to insurance if insurance selection is saved to store", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
|
|
@ -332,6 +357,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
// Ensure that query param isn't overriding selection
|
// Ensure that query param isn't overriding selection
|
||||||
wrapper.vm.$route = { query: null };
|
wrapper.vm.$route = { query: null };
|
||||||
|
|
||||||
|
|
@ -348,8 +374,6 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("should default to cash if cash selection is saved to store", async () => {
|
test("should default to cash if cash selection is saved to store", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
|
|
@ -363,6 +387,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
// Ensure that query param isn't overriding selection
|
// Ensure that query param isn't overriding selection
|
||||||
wrapper.vm.$route = { query: null };
|
wrapper.vm.$route = { query: null };
|
||||||
|
|
||||||
|
|
@ -380,8 +405,6 @@ describe("quote.vue", () => {
|
||||||
test("should default to cash if total economy package price is under $500", async () => {
|
test("should default to cash if total economy package price is under $500", async () => {
|
||||||
// Also needs no query parameter or previous selection in store to be present
|
// Also needs no query parameter or previous selection in store to be present
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
|
|
@ -396,6 +419,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mockTierOnePrice = 200;
|
mockTierOnePrice = 200;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
// Ensure that query param isn't overriding selection
|
// Ensure that query param isn't overriding selection
|
||||||
wrapper.vm.$route = { query: null };
|
wrapper.vm.$route = { query: null };
|
||||||
|
|
||||||
|
|
@ -413,7 +437,6 @@ describe("quote.vue", () => {
|
||||||
test("should default to insurance if total economy package price is over $500", async () => {
|
test("should default to insurance if total economy package price is over $500", async () => {
|
||||||
// Also needs no query parameter or previous selection in store to be present
|
// Also needs no query parameter or previous selection in store to be present
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
|
|
@ -429,6 +452,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mockTierOnePrice = 505;
|
mockTierOnePrice = 505;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
// Ensure that query param isn't overriding selection
|
// Ensure that query param isn't overriding selection
|
||||||
wrapper.vm.$route = { query: null };
|
wrapper.vm.$route = { query: null };
|
||||||
|
|
||||||
|
|
@ -445,7 +469,7 @@ describe("quote.vue", () => {
|
||||||
});
|
});
|
||||||
test("Should default to insurence if user Service zip is from certain States", async () => {
|
test("Should default to insurence if user Service zip is from certain States", async () => {
|
||||||
//Arrange
|
//Arrange
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
store.getters = {
|
store.getters = {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
|
|
@ -459,6 +483,7 @@ describe("quote.vue", () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
wrapper.vm.$route = { query: null };
|
wrapper.vm.$route = { query: null };
|
||||||
//Act
|
//Act
|
||||||
await quote.beforeRouteEnter.call(
|
await quote.beforeRouteEnter.call(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
:availableLineItems="availableLineItems"
|
:availableLineItems="availableLineItems"
|
||||||
:isInsuranceSelected="isInsuranceSelected"
|
:isInsuranceSelected="isInsuranceSelected"
|
||||||
@vapsItemsSelected="vapsItemsSelectedAction"
|
@vapsItemsSelected="vapsItemsSelectedAction"
|
||||||
|
:activePromos="allActivePromos"
|
||||||
v-on="{ 'buttonEvent.openModal': openModalAction }"
|
v-on="{ 'buttonEvent.openModal': openModalAction }"
|
||||||
validationRules="option-required"
|
validationRules="option-required"
|
||||||
isRequired />
|
isRequired />
|
||||||
|
|
@ -79,6 +80,10 @@ import { Form, defineRule } from "vee-validate";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
import { applicationConfig } from "@/constants/application-config";
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
|
import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
|
||||||
|
import { revalidatePromosAndValidateNewPromo } from "@/helpers/promotions-helper";
|
||||||
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||||
|
import { setTransitionHooks } from "vue";
|
||||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -122,14 +127,12 @@ export default {
|
||||||
];
|
];
|
||||||
|
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
const clonedGlassParts = store.getters.order.lineItems.glassParts
|
const nullSafeGlassParts = store.getters.order.lineItems.glassParts ?? [];
|
||||||
? JSON.parse(JSON.stringify(store.getters.order.lineItems.glassParts))
|
|
||||||
: [];
|
|
||||||
const availableLineItems = [
|
const availableLineItems = [
|
||||||
resultMap.rainDefense,
|
resultMap.rainDefense,
|
||||||
...resultMap.supportingItems,
|
...resultMap.supportingItems,
|
||||||
...resultMap.wipers,
|
...resultMap.wipers,
|
||||||
...clonedGlassParts,
|
...nullSafeGlassParts,
|
||||||
];
|
];
|
||||||
|
|
||||||
const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
|
const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
|
@ -146,13 +149,26 @@ export default {
|
||||||
resultMap.supportingItems,
|
resultMap.supportingItems,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Promo logic
|
||||||
|
const promoCodeFromQueryString = getQuerystringParameter(queryStrings.PROMO);
|
||||||
|
|
||||||
|
const { validatePromoResponse, revalidatePromoResponse } =
|
||||||
|
await revalidatePromosAndValidateNewPromo(
|
||||||
|
promoCodeFromQueryString,
|
||||||
|
pricingResults,
|
||||||
|
"quote"
|
||||||
|
);
|
||||||
|
// End of promo logic
|
||||||
|
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
vm.pricedGlassParts = clonedGlassParts;
|
vm.pricedGlassParts = nullSafeGlassParts;
|
||||||
vm.supportingItems = resultMap.supportingItems;
|
vm.supportingItems = resultMap.supportingItems;
|
||||||
vm.availableLineItems = pricingResults;
|
vm.availableLineItems = pricingResults;
|
||||||
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
|
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
|
||||||
|
vm.newValidatedPromos = validatePromoResponse?.data.orderPromos;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -162,8 +178,18 @@ export default {
|
||||||
availableLineItems: null,
|
availableLineItems: null,
|
||||||
supportingItems: null,
|
supportingItems: null,
|
||||||
pricedGlassParts: null,
|
pricedGlassParts: null,
|
||||||
|
newValidatedPromos: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
allActivePromos() {
|
||||||
|
const allActivePromos = [];
|
||||||
|
if (this.newValidatedPromos) allActivePromos.push(...this.newValidatedPromos);
|
||||||
|
if (this.$store.getters.order.lineItems.promos)
|
||||||
|
allActivePromos.push(...this.$store.getters.order.lineItems.promos);
|
||||||
|
return allActivePromos;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openModalAction(modalName) {
|
openModalAction(modalName) {
|
||||||
this.$refs[modalName].openModal();
|
this.$refs[modalName].openModal();
|
||||||
|
|
@ -239,6 +265,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
|
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
|
||||||
|
this.dispatchStoreAction(this.storeActions.SAVE_PROMOS, this.newValidatedPromos, false);
|
||||||
|
|
||||||
const payment = this.$store.getters.payment;
|
const payment = this.$store.getters.payment;
|
||||||
if (payment.isInsurance) {
|
if (payment.isInsurance) {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ import {
|
||||||
containsLineItemWithPartType,
|
containsLineItemWithPartType,
|
||||||
findLineItemsWithPartType,
|
findLineItemsWithPartType,
|
||||||
} from "@/helpers/service-package-helper";
|
} from "@/helpers/service-package-helper";
|
||||||
|
import {
|
||||||
|
getPromosThatMatchLineItemsOnOrder,
|
||||||
|
removeVapsPromosFromPromoArray,
|
||||||
|
} from "@/helpers/promotions-helper";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "servicePackageQuestion",
|
name: "servicePackageQuestion",
|
||||||
|
|
@ -40,6 +44,7 @@ export default {
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
isInsuranceSelected: Boolean,
|
isInsuranceSelected: Boolean,
|
||||||
availableLineItems: null,
|
availableLineItems: null,
|
||||||
|
activePromos: null,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -86,8 +91,11 @@ export default {
|
||||||
buttonLabel: this.getHeaderTextFromCms(answer.SubWidgetName),
|
buttonLabel: this.getHeaderTextFromCms(answer.SubWidgetName),
|
||||||
buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.SubWidgetName),
|
buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.SubWidgetName),
|
||||||
buttonBodyCopy: this.getBodyTextFromCms(answer.SubWidgetName),
|
buttonBodyCopy: this.getBodyTextFromCms(answer.SubWidgetName),
|
||||||
buttonAuxillaryCopy: this.getPackagePriceString(answer.Name),
|
buttonAuxillaryCopy: this.getDiscountedPackagePriceString(answer.Name),
|
||||||
buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName),
|
buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName),
|
||||||
|
additionalButtonData: {
|
||||||
|
strikeThroughPrice: this.getPackagePriceString(answer.Name),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
return modifiedAnswers;
|
return modifiedAnswers;
|
||||||
},
|
},
|
||||||
|
|
@ -161,21 +169,33 @@ export default {
|
||||||
return this.processIfStatements(footerText, "custom", this.getCustomValueFromString);
|
return this.processIfStatements(footerText, "custom", this.getCustomValueFromString);
|
||||||
},
|
},
|
||||||
getPackagePriceString(packageName) {
|
getPackagePriceString(packageName) {
|
||||||
const formattedPriceFloat = parseFloat(this.getPackagePrice(packageName)).toFixed(2);
|
const formattedPriceFloat = parseFloat(
|
||||||
|
this.getPackagePrice(packageName, { discountedPrice: false })
|
||||||
|
).toFixed(2);
|
||||||
|
return "$" + formattedPriceFloat;
|
||||||
|
},
|
||||||
|
getDiscountedPackagePriceString(packageName) {
|
||||||
|
const formattedPriceFloat = parseFloat(
|
||||||
|
this.getPackagePrice(packageName, { discountedPrice: true })
|
||||||
|
).toFixed(2);
|
||||||
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
|
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
|
||||||
},
|
},
|
||||||
getPackagePrice(packageName) {
|
getPackagePrice(packageName, { discountedPrice = false }) {
|
||||||
|
const lineItemsToPrice = [...this.nullSafeAvailableLineItems];
|
||||||
|
if (discountedPrice) {
|
||||||
|
lineItemsToPrice.push(...removeVapsPromosFromPromoArray(this.activePromos));
|
||||||
|
}
|
||||||
let priceFloat = this.isInsuranceSelected
|
let priceFloat = this.isInsuranceSelected
|
||||||
? 0
|
? 0
|
||||||
: baseMixin.methods.getTierOnePackagePrice(
|
: baseMixin.methods.getTierOnePackagePrice(
|
||||||
baseMixin.methods.filterOutFees(this.nullSafeAvailableLineItems)
|
baseMixin.methods.filterOutFees(lineItemsToPrice)
|
||||||
);
|
);
|
||||||
|
|
||||||
priceFloat += this.getVapsPrice(packageName);
|
priceFloat += this.getVapsPrice(packageName, discountedPrice);
|
||||||
|
|
||||||
return priceFloat;
|
return priceFloat;
|
||||||
},
|
},
|
||||||
getVapsPrice(packageName) {
|
getVapsPrice(packageName, applyPromoDiscounts = false) {
|
||||||
const vapsItems = this.getVapsLineItemsForSelectedPackage(packageName);
|
const vapsItems = this.getVapsLineItemsForSelectedPackage(packageName);
|
||||||
|
|
||||||
let price = 0;
|
let price = 0;
|
||||||
|
|
@ -184,6 +204,16 @@ export default {
|
||||||
price += baseMixin.methods.getTotalLineItemPrice(item);
|
price += baseMixin.methods.getTotalLineItemPrice(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (applyPromoDiscounts && this.activePromos) {
|
||||||
|
const relevantPromos = getPromosThatMatchLineItemsOnOrder(
|
||||||
|
this.activePromos,
|
||||||
|
vapsItems
|
||||||
|
);
|
||||||
|
relevantPromos.forEach((promo) => {
|
||||||
|
price += baseMixin.methods.getTotalLineItemPrice(promo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return price;
|
return price;
|
||||||
},
|
},
|
||||||
selectDefaultPackage() {
|
selectDefaultPackage() {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
|
||||||
<template v-if="ChangeShopLink.length">
|
<template v-if="ChangeShopLink.length">
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,18 @@ export async function getServiceabilityDetails(serviceZipCode, lineItems, pageNa
|
||||||
return Promise.resolve(serviceabilityDetails);
|
return Promise.resolve(serviceabilityDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getShopProviderData(serviceZipCode) {
|
||||||
|
const shopProviderData = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.GET_PROVIDERS,
|
||||||
|
{
|
||||||
|
serviceZipCode: serviceZipCode,
|
||||||
|
},
|
||||||
|
"service-location"
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.resolve(shopProviderData);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getAvailabilityRating(
|
export async function getAvailabilityRating(
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
|
|
|
||||||
|
|
@ -145,9 +145,6 @@ export default {
|
||||||
alertInvalidZipWidgetName: String,
|
alertInvalidZipWidgetName: String,
|
||||||
customComponentId: String,
|
customComponentId: String,
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
onZipUpdateCallback: {
|
|
||||||
type: Function,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
mobileLocationLinkPromptText() {
|
mobileLocationLinkPromptText() {
|
||||||
|
|
@ -256,10 +253,7 @@ export default {
|
||||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
||||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||||
|
|
||||||
if (this.onZipUpdateCallback) {
|
// update the page level model
|
||||||
await this.onZipUpdateCallback(serviceZipCode);
|
|
||||||
}
|
|
||||||
// Update the page level model
|
|
||||||
this.$emit("update:modelValue", this.internalModel);
|
this.$emit("update:modelValue", this.internalModel);
|
||||||
|
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,7 @@ import { getMountOptions } from "@/helpers/unit-test-helper";
|
||||||
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
import {
|
import { getServiceabilityDetails } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||||
getServiceabilityDetails,
|
|
||||||
Provider,
|
|
||||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
|
||||||
|
|
||||||
// Define Mocks
|
// Define Mocks
|
||||||
jest.mock("@/helpers/cms-content-helper", () => ({
|
jest.mock("@/helpers/cms-content-helper", () => ({
|
||||||
|
|
@ -21,7 +18,7 @@ jest.mock("@/helpers/cms-content-helper", () => ({
|
||||||
|
|
||||||
jest.mock("./classes/provider");
|
jest.mock("./classes/provider");
|
||||||
|
|
||||||
const mockGetPricedMobileFeePart = (mockServiceZipCode) => {
|
let mockGetPricedMobileFeePart = (mockServiceZipCode) => {
|
||||||
let mobileFeePart = {};
|
let mobileFeePart = {};
|
||||||
|
|
||||||
if (mockServiceZipCode === "43235") {
|
if (mockServiceZipCode === "43235") {
|
||||||
|
|
@ -38,7 +35,7 @@ const mockGetPricedMobileFeePart = (mockServiceZipCode) => {
|
||||||
return Promise.resolve(mobileFeePart);
|
return Promise.resolve(mobileFeePart);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockGetServiceabilityDetails = (mockServiceZipCode) => {
|
let mockGetServiceabilityDetails = (mockServiceZipCode) => {
|
||||||
const serviceabilityDetails = {
|
const serviceabilityDetails = {
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
|
|
@ -49,6 +46,17 @@ const mockGetServiceabilityDetails = (mockServiceZipCode) => {
|
||||||
return Promise.resolve(serviceabilityDetails);
|
return Promise.resolve(serviceabilityDetails);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mockGetShopProviderData = (mockServiceZipCode) => {
|
||||||
|
const result = {
|
||||||
|
data: {
|
||||||
|
mobileProviderNumber: "001820",
|
||||||
|
shopProviders: [{}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise.resolve(result);
|
||||||
|
};
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
"@/layouts/service-location/helpers/service-location-helper/service-location-helper",
|
"@/layouts/service-location/helpers/service-location-helper/service-location-helper",
|
||||||
() => ({
|
() => ({
|
||||||
|
|
@ -58,6 +66,9 @@ jest.mock(
|
||||||
getServiceabilityDetails: jest.fn((mockServiceZipCode) => {
|
getServiceabilityDetails: jest.fn((mockServiceZipCode) => {
|
||||||
return mockGetServiceabilityDetails(mockServiceZipCode);
|
return mockGetServiceabilityDetails(mockServiceZipCode);
|
||||||
}),
|
}),
|
||||||
|
getShopProviderData: jest.fn((mockServiceZipCode) => {
|
||||||
|
return mockGetShopProviderData(mockServiceZipCode);
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -554,14 +565,13 @@ describe("service-location.vue", () => {
|
||||||
describe("should be logical AND when recalibration is defined.", () => {
|
describe("should be logical AND when recalibration is defined.", () => {
|
||||||
test("T & T => T", async () => {
|
test("T & T => T", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -582,14 +592,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("T & F => F", async () => {
|
test("T & F => F", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -610,14 +619,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("F & T => F", async () => {
|
test("F & T => F", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -638,14 +646,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("F & F => F", async () => {
|
test("F & F => F", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -666,14 +673,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableMobileOnly should be true if mobile is true and inshop is false.", async () => {
|
test("displayServiceableMobileOnly should be true if mobile is true and inshop is false.", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -695,14 +701,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableMobileOnly should be false if mobile is false.", async () => {
|
test("displayServiceableMobileOnly should be false if mobile is false.", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -724,14 +729,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableMobileOnly should be false if inShop is true", async () => {
|
test("displayServiceableMobileOnly should be false if inShop is true", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -753,14 +757,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayNoShopsAlert should be true if inShop is false and mobile is false", async () => {
|
test("displayNoShopsAlert should be true if inShop is false and mobile is false", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -782,14 +785,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayNoShopsAlert should be false if inShop is true and mobile is true", async () => {
|
test("displayNoShopsAlert should be false if inShop is true and mobile is true", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -811,14 +813,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayNoShopsAlert should be false if inShop is true", async () => {
|
test("displayNoShopsAlert should be false if inShop is true", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -840,14 +841,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayNoShopsAlert should be false if mobile is true", async () => {
|
test("displayNoShopsAlert should be false if mobile is true", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -869,14 +869,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableInshopOnly should be true if inshop is true and mobile is false", async () => {
|
test("displayServiceableInshopOnly should be true if inshop is true and mobile is false", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -898,14 +897,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableInshopOnly should be false if inshop is false", async () => {
|
test("displayServiceableInshopOnly should be false if inshop is false", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: false,
|
isGlassServiceableInshop: false,
|
||||||
isRecalibrationServiceableInshop: false,
|
isRecalibrationServiceableInshop: false,
|
||||||
isGlassServiceableMobile: false,
|
isGlassServiceableMobile: false,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -927,14 +925,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableInshopOnly should be false if mobile is true", async () => {
|
test("displayServiceableInshopOnly should be false if mobile is true", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: true,
|
isRecalibrationServiceableMobile: true,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
@ -956,14 +953,13 @@ describe("service-location.vue", () => {
|
||||||
|
|
||||||
test("displayServiceableInshopOnly should be false in the dual/static recalibration scenario", async () => {
|
test("displayServiceableInshopOnly should be false in the dual/static recalibration scenario", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getServiceabilityDetails.mockImplementation(() =>
|
mockGetServiceabilityDetails = () =>
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
isGlassServiceableInshop: true,
|
isGlassServiceableInshop: true,
|
||||||
isRecalibrationServiceableInshop: true,
|
isRecalibrationServiceableInshop: true,
|
||||||
isGlassServiceableMobile: true,
|
isGlassServiceableMobile: true,
|
||||||
isRecalibrationServiceableMobile: false,
|
isRecalibrationServiceableMobile: false,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" class="mt-5" />
|
||||||
|
|
||||||
|
|
@ -19,8 +19,7 @@
|
||||||
@updated-serviceability="setServiceabilityDetails"
|
@updated-serviceability="setServiceabilityDetails"
|
||||||
@updated-contains-military-base="setContainsMilitaryBase"
|
@updated-contains-military-base="setContainsMilitaryBase"
|
||||||
linkWidgetName="ServiceZipLinkWidget"
|
linkWidgetName="ServiceZipLinkWidget"
|
||||||
modalWidgetName="ServiceZipModalWidget"
|
modalWidgetName="ServiceZipModalWidget" />
|
||||||
:onZipUpdateCallback="reloadShopData" />
|
|
||||||
|
|
||||||
<alert
|
<alert
|
||||||
ref="alertMilitaryBaseZip"
|
ref="alertMilitaryBaseZip"
|
||||||
|
|
@ -59,7 +58,7 @@
|
||||||
alertClass="alert-warning" />
|
alertClass="alert-warning" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center appointment-type">
|
<div class="row justify-content-center appointment-type">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<appointmentTypeQuestion
|
<appointmentTypeQuestion
|
||||||
v-model="selectedAppointmentType"
|
v-model="selectedAppointmentType"
|
||||||
|
|
@ -73,7 +72,7 @@
|
||||||
validationRules="option-required" />
|
validationRules="option-required" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<mobileLocationModalQuestions
|
<mobileLocationModalQuestions
|
||||||
customComponentId="mobileLocationQuestions"
|
customComponentId="mobileLocationQuestions"
|
||||||
|
|
@ -86,16 +85,15 @@
|
||||||
validationRules="mobile-location-required"
|
validationRules="mobile-location-required"
|
||||||
ref="mobileLocationQuestions"
|
ref="mobileLocationQuestions"
|
||||||
linkWidgetName="MobileLocationLinkWidget"
|
linkWidgetName="MobileLocationLinkWidget"
|
||||||
modalWidgetName="MobileLocationModalWidget"
|
modalWidgetName="MobileLocationModalWidget" />
|
||||||
:onZipUpdateCallback="reloadShopData" />
|
|
||||||
|
|
||||||
<shopQuestion
|
<shopQuestion
|
||||||
ref="shopQuestion"
|
ref="shopQuestion"
|
||||||
v-show="isShopQuestionDisplayed"
|
v-show="isShopQuestionDisplayed"
|
||||||
v-model="selectedProvider"
|
v-model="selectedProvider"
|
||||||
:selectedAppointmentType="selectedAppointmentType"
|
:selectedAppointmentType="selectedAppointmentType"
|
||||||
|
:shopProviderData="shopProviderData"
|
||||||
:isDisplayed="isShopQuestionDisplayed"
|
:isDisplayed="isShopQuestionDisplayed"
|
||||||
@updated-shop-list="setUpdatedShopList"
|
|
||||||
cmsWidgetName="ShopQuestionWidget" />
|
cmsWidgetName="ShopQuestionWidget" />
|
||||||
|
|
||||||
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />
|
||||||
|
|
@ -135,6 +133,7 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import {
|
import {
|
||||||
getPricedMobileFeePart,
|
getPricedMobileFeePart,
|
||||||
getServiceabilityDetails,
|
getServiceabilityDetails,
|
||||||
|
getShopProviderData,
|
||||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||||
|
|
||||||
import { Provider } from "@/layouts/service-location/classes/provider";
|
import { Provider } from "@/layouts/service-location/classes/provider";
|
||||||
|
|
@ -180,7 +179,7 @@ export default {
|
||||||
mobileFeePart: null,
|
mobileFeePart: null,
|
||||||
zipContainsMilitaryBase: false,
|
zipContainsMilitaryBase: false,
|
||||||
zipCodeCtu: null,
|
zipCodeCtu: null,
|
||||||
providerData: null,
|
shopProviderData: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -188,7 +187,10 @@ export default {
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
|
|
||||||
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
|
||||||
const getZipCodeData = baseMixin.methods.getZipCodeData(serviceZipCode, "service-location");
|
const zipCodeDataPromise = baseMixin.methods.getZipCodeData(
|
||||||
|
serviceZipCode,
|
||||||
|
"service-location"
|
||||||
|
);
|
||||||
|
|
||||||
const serviceabilityDetailsPromise = getServiceabilityDetails(
|
const serviceabilityDetailsPromise = getServiceabilityDetails(
|
||||||
serviceZipCode,
|
serviceZipCode,
|
||||||
|
|
@ -198,7 +200,7 @@ export default {
|
||||||
|
|
||||||
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode, "service-location");
|
const mobileFeePartPromise = getPricedMobileFeePart(serviceZipCode, "service-location");
|
||||||
|
|
||||||
const shopQuestionInitialDataPromise = shopQuestion.methods.loadInitialData(serviceZipCode);
|
const shopProviderDataPromise = getShopProviderData(serviceZipCode); // shopQuestion.methods.loadInitialData(serviceZipCode);
|
||||||
|
|
||||||
// Settle promises and get results
|
// Settle promises and get results
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
|
|
@ -206,6 +208,10 @@ export default {
|
||||||
resultKey: "cmsContent",
|
resultKey: "cmsContent",
|
||||||
promise: cmsContentPromise,
|
promise: cmsContentPromise,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
resultKey: "zipCodeData",
|
||||||
|
promise: zipCodeDataPromise,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
resultKey: "mobileFeePart",
|
resultKey: "mobileFeePart",
|
||||||
promise: mobileFeePartPromise,
|
promise: mobileFeePartPromise,
|
||||||
|
|
@ -215,12 +221,8 @@ export default {
|
||||||
promise: serviceabilityDetailsPromise,
|
promise: serviceabilityDetailsPromise,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
resultKey: "zipCodeData",
|
resultKey: "shopProviderData",
|
||||||
promise: getZipCodeData,
|
promise: shopProviderDataPromise,
|
||||||
},
|
|
||||||
{
|
|
||||||
resultKey: "shopQuestionInitialData",
|
|
||||||
promise: shopQuestionInitialDataPromise,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -232,14 +234,9 @@ export default {
|
||||||
vm.setData(
|
vm.setData(
|
||||||
resultMap.zipCodeData,
|
resultMap.zipCodeData,
|
||||||
resultMap.serviceabilityDetails,
|
resultMap.serviceabilityDetails,
|
||||||
resultMap.mobileFeePart
|
resultMap.mobileFeePart,
|
||||||
|
resultMap.shopProviderData
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize the Shop Question component
|
|
||||||
vm.$refs.shopQuestion.initializeComponent(resultMap.shopQuestionInitialData);
|
|
||||||
|
|
||||||
// Initialize the page level shop data
|
|
||||||
vm.providerData = resultMap.shopQuestionInitialData;
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -254,7 +251,7 @@ export default {
|
||||||
if (newValue.zipCode !== this.zipCode) {
|
if (newValue.zipCode !== this.zipCode) {
|
||||||
this.resetMobileLocation();
|
this.resetMobileLocation();
|
||||||
this.selectedAppointmentType = null;
|
this.selectedAppointmentType = null;
|
||||||
this.selectedProvider = null;
|
this.selectedProvider = new Provider();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = newValue.state;
|
this.state = newValue.state;
|
||||||
|
|
@ -277,20 +274,16 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
set: function (newValue) {
|
set: function (newValue) {
|
||||||
this.streetAddress = newValue.addressQuestions.streetAddress;
|
if (newValue.addressQuestions.zipCode !== this.zipCode) {
|
||||||
this.apartmentNumberOrBusinessName =
|
getShopProviderData(newValue.addressQuestions.zipCode).then((result) => {
|
||||||
newValue.addressQuestions.apartmentNumberOrBusinessName;
|
this.shopProviderData = result.data;
|
||||||
this.city = newValue.addressQuestions.city;
|
this.selectedProvider = new Provider(
|
||||||
this.state = newValue.addressQuestions.state;
|
this.shopProviderData.mobileProviderNumber
|
||||||
this.zipCode = newValue.addressQuestions.zipCode;
|
);
|
||||||
this.isVehicleProtected = newValue.isVehicleProtected;
|
});
|
||||||
|
|
||||||
if (newValue.zipCode !== this.zipCode) {
|
|
||||||
if (!this.selectedAppointmentType == "Mobile") {
|
|
||||||
this.selectedAppointmentType = null;
|
|
||||||
}
|
|
||||||
this.selectedProvider = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setMobileLocation(newValue);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isServiceableMobile() {
|
isServiceableMobile() {
|
||||||
|
|
@ -355,7 +348,7 @@ export default {
|
||||||
store.getters.payment.isInsurance !== null
|
store.getters.payment.isInsurance !== null
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
setData(zipCodeData, serviceabilityDetails, mobileFeePart) {
|
setData(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) {
|
||||||
if (zipCodeData) {
|
if (zipCodeData) {
|
||||||
this.zipContainsMilitaryBase = zipCodeData.containsMilitaryBase;
|
this.zipContainsMilitaryBase = zipCodeData.containsMilitaryBase;
|
||||||
this.zipCodeCtu = zipCodeData.zipCodeCtu;
|
this.zipCodeCtu = zipCodeData.zipCodeCtu;
|
||||||
|
|
@ -368,6 +361,10 @@ export default {
|
||||||
if (mobileFeePart) {
|
if (mobileFeePart) {
|
||||||
this.mobileFeePart = mobileFeePart;
|
this.mobileFeePart = mobileFeePart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shopProviderData) {
|
||||||
|
this.shopProviderData = shopProviderData;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
setContainsMilitaryBase(val) {
|
setContainsMilitaryBase(val) {
|
||||||
if (this.zipContainsMilitaryBase !== val) {
|
if (this.zipContainsMilitaryBase !== val) {
|
||||||
|
|
@ -416,12 +413,18 @@ export default {
|
||||||
this.isRecalibrationServiceableMobile =
|
this.isRecalibrationServiceableMobile =
|
||||||
serviceabilityDetails.isRecalibrationServiceableMobile;
|
serviceabilityDetails.isRecalibrationServiceableMobile;
|
||||||
},
|
},
|
||||||
|
setMobileLocation(mobileLocation) {
|
||||||
|
this.streetAddress = mobileLocation.addressQuestions.streetAddress;
|
||||||
|
this.apartmentNumberOrBusinessName =
|
||||||
|
mobileLocation.addressQuestions.apartmentNumberOrBusinessName;
|
||||||
|
this.city = mobileLocation.addressQuestions.city;
|
||||||
|
this.state = mobileLocation.addressQuestions.state;
|
||||||
|
this.zipCode = mobileLocation.addressQuestions.zipCode;
|
||||||
|
this.isVehicleProtected = mobileLocation.isVehicleProtected;
|
||||||
|
},
|
||||||
async reloadShopData(zipCode) {
|
async reloadShopData(zipCode) {
|
||||||
await this.$refs.shopQuestion.reloadShopData(zipCode);
|
await this.$refs.shopQuestion.reloadShopData(zipCode);
|
||||||
},
|
},
|
||||||
setUpdatedShopList(providerData) {
|
|
||||||
this.providerData = providerData;
|
|
||||||
},
|
|
||||||
openRecalibrationInformationModal() {
|
openRecalibrationInformationModal() {
|
||||||
this.recalibrationInformationModal.openModal();
|
this.recalibrationInformationModal.openModal();
|
||||||
},
|
},
|
||||||
|
|
@ -494,21 +497,20 @@ export default {
|
||||||
this.updateAndSaveSupportingItems();
|
this.updateAndSaveSupportingItems();
|
||||||
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
|
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
|
||||||
},
|
},
|
||||||
|
openModalAction(modalName) {
|
||||||
|
this.$refs[modalName].openModal();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedAppointmentType: {
|
selectedAppointmentType: {
|
||||||
handler(newValue) {
|
async handler(newValue) {
|
||||||
if (newValue === "Mobile") {
|
if (newValue === "Mobile") {
|
||||||
this.selectedProvider = new Provider(this.providerData.mobileProviderNumber);
|
getShopProviderData(this.zipCode).then(async (result) => {
|
||||||
} else {
|
this.shopProviderData = result.data;
|
||||||
this.selectedProvider = new Provider();
|
this.selectedProvider = new Provider(
|
||||||
}
|
this.shopProviderData.mobileProviderNumber
|
||||||
},
|
);
|
||||||
},
|
});
|
||||||
providerData: {
|
|
||||||
handler(newValue) {
|
|
||||||
if (newValue === "Mobile") {
|
|
||||||
this.selectedProvider = new Provider(this.providerData.mobileProviderNumber);
|
|
||||||
} else {
|
} else {
|
||||||
this.selectedProvider = new Provider();
|
this.selectedProvider = new Provider();
|
||||||
}
|
}
|
||||||
|
|
@ -535,7 +537,6 @@ export default {
|
||||||
.appointment-type-question {
|
.appointment-type-question {
|
||||||
.button-question {
|
.button-question {
|
||||||
.col {
|
.col {
|
||||||
padding: 0 0.5rem;
|
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
padding: 0 0.75rem;
|
padding: 0 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,9 +68,6 @@ export default {
|
||||||
},
|
},
|
||||||
linkWidgetName: String,
|
linkWidgetName: String,
|
||||||
modalWidgetName: String,
|
modalWidgetName: String,
|
||||||
onZipUpdateCallback: {
|
|
||||||
type: Function,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
serviceZipLinkText() {
|
serviceZipLinkText() {
|
||||||
|
|
@ -164,13 +161,9 @@ export default {
|
||||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
||||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||||
|
|
||||||
// Update the page level model
|
// update the page level model
|
||||||
this.$emit("update:modelValue", this.internalModel);
|
this.$emit("update:modelValue", this.internalModel);
|
||||||
|
|
||||||
if (this.onZipUpdateCallback) {
|
|
||||||
await this.onZipUpdateCallback(serviceZipCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ const mockCmsContent = {
|
||||||
|
|
||||||
const cmsWidgetName = "ShopQuestionWidget";
|
const cmsWidgetName = "ShopQuestionWidget";
|
||||||
const shopQuestionInitialData = {
|
const shopQuestionInitialData = {
|
||||||
|
mobileProviderNumber: "001820",
|
||||||
shopProviders: [
|
shopProviders: [
|
||||||
{
|
{
|
||||||
address: {
|
address: {
|
||||||
|
|
@ -162,15 +163,18 @@ describe("shop-question.vue", () => {
|
||||||
selectedAppointmentType: "Dropoff",
|
selectedAppointmentType: "Dropoff",
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isDisplayed: true,
|
isDisplayed: true,
|
||||||
|
shopProviderData: null,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
await wrapper.setProps({
|
||||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
shopProviderData: shopQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
|
@ -199,47 +203,6 @@ describe("shop-question.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should display the 'Show more locations' link when there are more than three locations to chose from", async () => {
|
it("Should display the 'Show more locations' link when there are more than three locations to chose from", async () => {
|
||||||
// Arrange
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
mixins: [mockMixin],
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
address: {
|
|
||||||
city: "POWELL",
|
|
||||||
country: "US",
|
|
||||||
state: "OH",
|
|
||||||
streetAddress: "3938 POWELL RD",
|
|
||||||
zipCode: "43065",
|
|
||||||
},
|
|
||||||
distanceInMiles: 16.2690495685233,
|
|
||||||
providerNumber: "003341",
|
|
||||||
},
|
|
||||||
serviceZipCode: "43081",
|
|
||||||
selectedAppointmentType: "Dropoff",
|
|
||||||
cmsWidgetName: cmsWidgetName,
|
|
||||||
isDisplayed: true,
|
|
||||||
},
|
|
||||||
mountOptions: {
|
|
||||||
attachTo: document.body,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(showMoreShopsLink.exists()).toBe(true);
|
|
||||||
expect(showMoreShopsLink.isVisible()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Should not display the 'Show more locations' link when there are fewer than three locations to chose from", async () => {
|
|
||||||
// Arrange
|
// Arrange
|
||||||
const alsoShopQuestionInitialData = {
|
const alsoShopQuestionInitialData = {
|
||||||
shopProviders: [
|
shopProviders: [
|
||||||
|
|
@ -276,6 +239,17 @@ describe("shop-question.vue", () => {
|
||||||
distanceInMiles: 11.738869544543,
|
distanceInMiles: 11.738869544543,
|
||||||
providerNumber: "003343",
|
providerNumber: "003343",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
address: {
|
||||||
|
city: "COLUMBUS",
|
||||||
|
country: "US",
|
||||||
|
state: "OH",
|
||||||
|
streetAddress: "1670 HARMON AVE",
|
||||||
|
zipCode: "43223",
|
||||||
|
},
|
||||||
|
distanceInMiles: 15.9727889297435,
|
||||||
|
providerNumber: "006747",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -297,6 +271,7 @@ describe("shop-question.vue", () => {
|
||||||
selectedAppointmentType: "Dropoff",
|
selectedAppointmentType: "Dropoff",
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isDisplayed: true,
|
isDisplayed: true,
|
||||||
|
shopProviderData: null,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
@ -304,17 +279,94 @@ describe("shop-question.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.initializeComponent(alsoShopQuestionInitialData);
|
await wrapper.setProps({
|
||||||
|
shopProviderData: alsoShopQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(showMoreShopsLink.exists()).toBe(false);
|
expect(showMoreShopsLink.exists()).toBeTruthy();
|
||||||
|
expect(showMoreShopsLink.isVisible()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should display the next three shops when the 'Show more location' link is clicked", async () => {
|
it("Should not display the 'Show more locations' link when there are fewer than three locations to chose from", async () => {
|
||||||
|
// Arrange
|
||||||
|
const alsoShopQuestionInitialData = {
|
||||||
|
shopProviders: [
|
||||||
|
{
|
||||||
|
address: {
|
||||||
|
city: "WESTERVILLE",
|
||||||
|
country: "US",
|
||||||
|
state: "OH",
|
||||||
|
streetAddress: "4403 EXECUTIVE PKWY",
|
||||||
|
zipCode: "43081",
|
||||||
|
},
|
||||||
|
distanceInMiles: 5.16769294095201,
|
||||||
|
providerNumber: "003335",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
address: {
|
||||||
|
city: "WORTHINGTON",
|
||||||
|
country: "US",
|
||||||
|
state: "OH",
|
||||||
|
streetAddress: "760 DEARBORN PARK LN",
|
||||||
|
zipCode: "43085",
|
||||||
|
},
|
||||||
|
distanceInMiles: 10.5865432478478,
|
||||||
|
providerNumber: "001820",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mixins: [mockMixin],
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
address: {
|
||||||
|
city: "POWELL",
|
||||||
|
country: "US",
|
||||||
|
state: "OH",
|
||||||
|
streetAddress: "3938 POWELL RD",
|
||||||
|
zipCode: "43065",
|
||||||
|
},
|
||||||
|
distanceInMiles: 16.2690495685233,
|
||||||
|
providerNumber: "003341",
|
||||||
|
},
|
||||||
|
serviceZipCode: "43081",
|
||||||
|
selectedAppointmentType: "Dropoff",
|
||||||
|
cmsWidgetName: cmsWidgetName,
|
||||||
|
isDisplayed: true,
|
||||||
|
shopProviderData: null,
|
||||||
|
},
|
||||||
|
mountOptions: {
|
||||||
|
attachTo: document.body,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await wrapper.setProps({
|
||||||
|
shopProviderData: alsoShopQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(showMoreShopsLink.exists()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: Add test to ensure the "Show more locations" link is not visible when all the existing shops are displayed.
|
||||||
|
|
||||||
|
it("Should display the next three shops when the 'Show more locations' link is clicked", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const displayedAnswers = [
|
const displayedAnswers = [
|
||||||
{
|
{
|
||||||
|
|
@ -355,6 +407,7 @@ describe("shop-question.vue", () => {
|
||||||
selectedAppointmentType: "Dropoff",
|
selectedAppointmentType: "Dropoff",
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
isDisplayed: true,
|
isDisplayed: true,
|
||||||
|
shopProviderData: null,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
@ -362,7 +415,9 @@ describe("shop-question.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
await wrapper.setProps({
|
||||||
|
shopProviderData: shopQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
|
@ -384,7 +439,7 @@ describe("shop-question.vue", () => {
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
showMoreShopsLink.trigger("click");
|
showMoreShopsLink.trigger("click-event");
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
|
|
@ -443,6 +498,7 @@ describe("shop-question.vue", () => {
|
||||||
serviceZipCode: "43081",
|
serviceZipCode: "43081",
|
||||||
selectedAppointmentType: "Dropoff",
|
selectedAppointmentType: "Dropoff",
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
|
shopProviderData: null,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
|
|
@ -450,7 +506,9 @@ describe("shop-question.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.initializeComponent(shopQuestionInitialData);
|
await wrapper.setProps({
|
||||||
|
shopProviderData: shopQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
@ -468,15 +526,18 @@ describe("shop-question.vue", () => {
|
||||||
modelValue: null,
|
modelValue: null,
|
||||||
selectedAppointmentType: "Dropoff",
|
selectedAppointmentType: "Dropoff",
|
||||||
cmsWidgetName: cmsWidgetName,
|
cmsWidgetName: cmsWidgetName,
|
||||||
|
shopProviderData: null,
|
||||||
},
|
},
|
||||||
mountOptions: {
|
mountOptions: {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Act
|
await wrapper.setProps({
|
||||||
await wrapper.vm.initializeComponent(shopQuestionInitialData);
|
shopProviderData: shopQuestionInitialData,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
await wrapper.vm.$nextTick();
|
await wrapper.vm.$nextTick();
|
||||||
|
|
||||||
expect(wrapper.vm.answers.length).toEqual(3);
|
expect(wrapper.vm.answers.length).toEqual(3);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
linkType="text"
|
linkType="text"
|
||||||
:text="showMoreShopsLinkText"
|
:text="showMoreShopsLinkText"
|
||||||
href="#!"
|
href="#!"
|
||||||
@click-event="getNextShopsFromList"
|
@click-event="getNextShopsFromList(3)"
|
||||||
:aria-label="showMoreShopsLinkText" />
|
:aria-label="showMoreShopsLinkText" />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
@ -43,8 +43,6 @@ import buttonQuestion from "@/digital-components/button-question/button-question
|
||||||
import shopListButton from "./shop-list-button/shop-list-button";
|
import shopListButton from "./shop-list-button/shop-list-button";
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
|
|
||||||
import { storeActions } from "@/constants/store-actions.js";
|
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { defineRule } from "vee-validate";
|
import { defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
|
@ -63,7 +61,6 @@ export default {
|
||||||
mixins: [baseMixin],
|
mixins: [baseMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
shopProviders: [],
|
|
||||||
shopListButton: shopListButton,
|
shopListButton: shopListButton,
|
||||||
answers: [],
|
answers: [],
|
||||||
shopIndex: 0,
|
shopIndex: 0,
|
||||||
|
|
@ -76,6 +73,7 @@ export default {
|
||||||
default: () => null,
|
default: () => null,
|
||||||
},
|
},
|
||||||
selectedAppointmentType: String,
|
selectedAppointmentType: String,
|
||||||
|
shopProviderData: Object,
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String,
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
isDisplayed: Boolean,
|
isDisplayed: Boolean,
|
||||||
|
|
@ -84,6 +82,9 @@ export default {
|
||||||
questionText() {
|
questionText() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||||
},
|
},
|
||||||
|
shopProviders() {
|
||||||
|
return this.shopProviderData?.shopProviders ?? [];
|
||||||
|
},
|
||||||
selectedValue: {
|
selectedValue: {
|
||||||
get: function () {
|
get: function () {
|
||||||
return this.modelValue;
|
return this.modelValue;
|
||||||
|
|
@ -124,22 +125,6 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadInitialData(serviceZipCode) {
|
|
||||||
return this.loadData(serviceZipCode);
|
|
||||||
},
|
|
||||||
loadData(serviceZipCode) {
|
|
||||||
return baseMixin.methods.dispatchStoreActionWithLogging(
|
|
||||||
storeActions.GET_PROVIDERS,
|
|
||||||
{
|
|
||||||
serviceZipCode: serviceZipCode,
|
|
||||||
},
|
|
||||||
"service-location"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
initializeComponent(shopQuestionInitialData) {
|
|
||||||
this.shopProviders = shopQuestionInitialData.shopProviders;
|
|
||||||
this.$emit("updated-shop-list", shopQuestionInitialData);
|
|
||||||
},
|
|
||||||
async getNextShopsFromList(numberToGet = 3) {
|
async getNextShopsFromList(numberToGet = 3) {
|
||||||
const shopIterator = (array, n) => {
|
const shopIterator = (array, n) => {
|
||||||
const l = array.length;
|
const l = array.length;
|
||||||
|
|
@ -199,15 +184,6 @@ export default {
|
||||||
this.answers = [];
|
this.answers = [];
|
||||||
this.shopIndex = 0;
|
this.shopIndex = 0;
|
||||||
},
|
},
|
||||||
async reloadShopData(serviceZipCode) {
|
|
||||||
const result = await this.loadData(serviceZipCode);
|
|
||||||
this.initializeComponent(result.data);
|
|
||||||
|
|
||||||
this.resetAnswers();
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
await this.getNextShopsFromList();
|
|
||||||
},
|
|
||||||
getSelectedProviderObject(providerNumber) {
|
getSelectedProviderObject(providerNumber) {
|
||||||
const provider =
|
const provider =
|
||||||
this.shopProviders?.find((provider) => provider.providerNumber == providerNumber) ??
|
this.shopProviders?.find((provider) => provider.providerNumber == providerNumber) ??
|
||||||
|
|
@ -239,19 +215,16 @@ export default {
|
||||||
shopProviders: {
|
shopProviders: {
|
||||||
async handler(newValue) {
|
async handler(newValue) {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
const selectedShopIndex = this.getSelectedProviderIndex(
|
||||||
|
newValue,
|
||||||
|
this.selectedProviderNumber
|
||||||
|
);
|
||||||
|
|
||||||
if (this.selectedAppointmentType) {
|
if (selectedShopIndex >= 3) {
|
||||||
const selectedShopIndex = this.getSelectedProviderIndex(
|
await this.getNextShopsFromList(selectedShopIndex + 1);
|
||||||
newValue,
|
} else {
|
||||||
this.selectedProviderNumber
|
await this.getNextShopsFromList();
|
||||||
);
|
await nextTick();
|
||||||
|
|
||||||
if (selectedShopIndex >= 3) {
|
|
||||||
await this.getNextShopsFromList(selectedShopIndex + 1);
|
|
||||||
} else {
|
|
||||||
await this.getNextShopsFromList();
|
|
||||||
await nextTick();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
:isDismissible="false" />
|
:isDismissible="false" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<damageLocationQuestion
|
<damageLocationQuestion
|
||||||
ref="damageLocation"
|
ref="damageLocation"
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
groupName="DamageLocationQuestion" />
|
groupName="DamageLocationQuestion" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<windshieldOptions
|
<windshieldOptions
|
||||||
class="windshield-options"
|
class="windshield-options"
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
validationRules="replace-options-required" />
|
validationRules="replace-options-required" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
|
@ -551,9 +551,19 @@ export default {
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.replace-options-question {
|
.replace-options-question {
|
||||||
|
.question-text {
|
||||||
|
margin: 1rem 0 0 0;
|
||||||
|
@include media-breakpoint-up(md) {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
.two-list-card-width {
|
.two-list-card-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 0 auto;
|
flex: 0 auto;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-lg-4">
|
<div class="col-md-6 col-lg-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
ref="vehicleBanner"
|
ref="vehicleBanner"
|
||||||
|
|
@ -17,20 +17,18 @@
|
||||||
<funnelSubHeader
|
<funnelSubHeader
|
||||||
ref="funnelSubHeader"
|
ref="funnelSubHeader"
|
||||||
cmsWidgetName="FunnelSubHeaderWidget" />
|
cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<div class="prevent-squish my-5">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col pb-0">
|
|
||||||
<alert
|
|
||||||
class="rounded border-0 shadow-sm"
|
|
||||||
alertClass="alert-warning"
|
|
||||||
cmsWidgetName="AlertWidget"
|
|
||||||
:isDismissible="false" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-6 col-xl-4 my-5">
|
||||||
|
<alert
|
||||||
|
class="rounded border-0 shadow-sm"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="AlertWidget"
|
||||||
|
:isDismissible="false" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<div v-for="(item, i) in PartsOrQuestions" :key="i">
|
<div v-for="(item, i) in PartsOrQuestions" :key="i">
|
||||||
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row justify-content-md-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6 col-xl-4">
|
<div class="col-md-6 col-xl-4">
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
class="mb-4 mt-5"
|
class="mb-2 mt-5"
|
||||||
cmsWidgetName="VinNumberQuestionWidget"
|
cmsWidgetName="VinNumberQuestionWidget"
|
||||||
v-model="vin"
|
v-model="vin"
|
||||||
customInputId="vin"
|
customInputId="vin"
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import store from "@/store";
|
||||||
import analyticsMixin from "@/mixins/analytics-mixin";
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
import { experimentTriggers } from "../constants/experiments";
|
import { experimentTriggers } from "../constants/experiments";
|
||||||
import { applicationConfig } from "../constants/application-config";
|
import { applicationConfig } from "../constants/application-config";
|
||||||
|
import { shouldStripPromoQueryString } from "@/helpers/promotions-helper";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
|
@ -283,7 +284,7 @@ async function navigate(
|
||||||
queryStringsObject[queryStrings.ZIP_CODE] = zip;
|
queryStringsObject[queryStrings.ZIP_CODE] = zip;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promo) {
|
if (promo && !shouldStripPromoQueryString(currentRoute.query.fmgPage)) {
|
||||||
queryStringsObject[queryStrings.PROMO] = promo;
|
queryStringsObject[queryStrings.PROMO] = promo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@ const getDefaultState = () => {
|
||||||
supportingItems: null,
|
supportingItems: null,
|
||||||
vaps: null,
|
vaps: null,
|
||||||
serverData: null,
|
serverData: null,
|
||||||
|
promos: null,
|
||||||
},
|
},
|
||||||
payment: {
|
payment: {
|
||||||
isInsurance: null,
|
isInsurance: null,
|
||||||
|
|
@ -95,6 +96,7 @@ const getDefaultState = () => {
|
||||||
parentAccountNumber: 0,
|
parentAccountNumber: 0,
|
||||||
payNowType: "cc",
|
payNowType: "cc",
|
||||||
piaErrorCode: null,
|
piaErrorCode: null,
|
||||||
|
inactivePromos: null,
|
||||||
},
|
},
|
||||||
schedule: {
|
schedule: {
|
||||||
date: null,
|
date: null,
|
||||||
|
|
@ -195,6 +197,12 @@ export const mutations = {
|
||||||
updateLineItemsServerData(state, serverData) {
|
updateLineItemsServerData(state, serverData) {
|
||||||
state.order.lineItems.serverData = serverData;
|
state.order.lineItems.serverData = serverData;
|
||||||
},
|
},
|
||||||
|
updateInactivePromos(state, inactivePromos) {
|
||||||
|
state.order.payment.inactivePromos = inactivePromos;
|
||||||
|
},
|
||||||
|
updatePromos(state, promos) {
|
||||||
|
state.order.lineItems.promos = promos;
|
||||||
|
},
|
||||||
updatePageData(state, pageData) {
|
updatePageData(state, pageData) {
|
||||||
state.applicationUser.pageData[pageData.page] = pageData.data;
|
state.applicationUser.pageData[pageData.page] = pageData.data;
|
||||||
},
|
},
|
||||||
|
|
@ -664,7 +672,10 @@ function getTimeSlotsAdditionalEventData(
|
||||||
) {
|
) {
|
||||||
var numberOfDays = null;
|
var numberOfDays = null;
|
||||||
if (firstAvailableAppointmentDateString)
|
if (firstAvailableAppointmentDateString)
|
||||||
numberOfDays = getDateDifferenceInDays(new Date(), firstAvailableAppointmentDateString);
|
numberOfDays = getDateDifferenceInDays(
|
||||||
|
new Date().toISOString().split("T")[0],
|
||||||
|
firstAvailableAppointmentDateString
|
||||||
|
);
|
||||||
|
|
||||||
if (shopAppointmentType)
|
if (shopAppointmentType)
|
||||||
return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join(
|
return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join(
|
||||||
|
|
@ -1235,10 +1246,10 @@ export const actions = {
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
getWipers(context, { pageNameToLog }) {
|
async getWipers(context, { pageNameToLog }) {
|
||||||
const carId = context.getters.vehicle.carId;
|
const carId = context.getters.vehicle.carId;
|
||||||
const serviceZipCode = context.getters.order.serviceLocation.zipCode;
|
const serviceZipCode = context.getters.order.serviceLocation.zipCode;
|
||||||
return globalMethods
|
const response = await globalMethods
|
||||||
.callHttpClient({
|
.callHttpClient({
|
||||||
method: endpoints.GetWipers.method,
|
method: endpoints.GetWipers.method,
|
||||||
endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`,
|
endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`,
|
||||||
|
|
@ -1249,15 +1260,20 @@ export const actions = {
|
||||||
// The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow
|
// The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
syncLineItemIds(response.data, context.getters.order.lineItems.vaps);
|
||||||
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
getRainDefense(context, { pageNameToLog }) {
|
async getRainDefense(context, { pageNameToLog }) {
|
||||||
return globalMethods.callHttpClient({
|
const response = await globalMethods.callHttpClient({
|
||||||
method: endpoints.GetRainDefense.method,
|
method: endpoints.GetRainDefense.method,
|
||||||
endpoint: endpoints.GetRainDefense.url,
|
endpoint: endpoints.GetRainDefense.url,
|
||||||
logApiCall: true,
|
logApiCall: true,
|
||||||
pageNameToLog: pageNameToLog,
|
pageNameToLog: pageNameToLog,
|
||||||
});
|
});
|
||||||
|
syncLineItemIds([response.data], context.getters.order.lineItems.vaps);
|
||||||
|
return response;
|
||||||
},
|
},
|
||||||
|
|
||||||
getMobileFeePart(context, { pageNameToLog }) {
|
getMobileFeePart(context, { pageNameToLog }) {
|
||||||
|
|
@ -1537,6 +1553,7 @@ export const actions = {
|
||||||
endpoint: endpoints.SaveSession.url,
|
endpoint: endpoints.SaveSession.url,
|
||||||
payload: {
|
payload: {
|
||||||
submitAfterSave: context.state.submitAfterSave,
|
submitAfterSave: context.state.submitAfterSave,
|
||||||
|
userAgent: navigator.userAgent,
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
crmCustomerId: applicationUser.crmCustomerId,
|
crmCustomerId: applicationUser.crmCustomerId,
|
||||||
experiments: applicationUser.experiments,
|
experiments: applicationUser.experiments,
|
||||||
|
|
@ -1582,6 +1599,7 @@ export const actions = {
|
||||||
supportingItems: lineItems.supportingItems,
|
supportingItems: lineItems.supportingItems,
|
||||||
vaps: lineItems.vaps,
|
vaps: lineItems.vaps,
|
||||||
serverData: lineItems.serverData,
|
serverData: lineItems.serverData,
|
||||||
|
promos: lineItems.promos,
|
||||||
},
|
},
|
||||||
payment: {
|
payment: {
|
||||||
InsuranceCoverage: {
|
InsuranceCoverage: {
|
||||||
|
|
@ -1589,6 +1607,7 @@ export const actions = {
|
||||||
},
|
},
|
||||||
isInsurance: order.payment.isInsurance,
|
isInsurance: order.payment.isInsurance,
|
||||||
parentAccountNumber: order.payment.parentAccountNumber,
|
parentAccountNumber: order.payment.parentAccountNumber,
|
||||||
|
inactivePromos: order.payment.inactivePromos,
|
||||||
},
|
},
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
streetAddress: order.serviceLocation.address,
|
streetAddress: order.serviceLocation.address,
|
||||||
|
|
@ -2007,17 +2026,21 @@ export const actions = {
|
||||||
if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
|
if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
|
||||||
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
}
|
}
|
||||||
|
addGuidToLineItemsIfNotAlreadyThere(supportingItems);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
|
||||||
},
|
},
|
||||||
|
|
||||||
saveSupportingItemsSuppressingStateResetting(context, supportingItems) {
|
saveSupportingItemsSuppressingStateResetting(context, supportingItems) {
|
||||||
|
addGuidToLineItemsIfNotAlreadyThere(supportingItems);
|
||||||
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
|
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
|
||||||
},
|
},
|
||||||
|
|
||||||
saveVaps(context, vaps) {
|
saveVaps(context, vaps) {
|
||||||
|
addGuidToLineItemsIfNotAlreadyThere(vaps);
|
||||||
context.commit(storeMutations.UPDATE_VAPS, vaps);
|
context.commit(storeMutations.UPDATE_VAPS, vaps);
|
||||||
},
|
},
|
||||||
|
savePromos(context, promos) {
|
||||||
|
context.commit(storeMutations.UPDATE_PROMOS, promos);
|
||||||
|
},
|
||||||
|
|
||||||
// Price order actions
|
// Price order actions
|
||||||
async priceOrderItemsAndSaveServerData(
|
async priceOrderItemsAndSaveServerData(
|
||||||
|
|
@ -2139,6 +2162,95 @@ export const actions = {
|
||||||
return taxedLineItems;
|
return taxedLineItems;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async validateOrderPromoAndSaveServerData(
|
||||||
|
context,
|
||||||
|
{ payload: { promoCode, addableVaps }, pageNameToLog }
|
||||||
|
) {
|
||||||
|
const order = context.getters.order;
|
||||||
|
addGuidToLineItemsIfNotAlreadyThere(addableVaps);
|
||||||
|
const requestObject = {
|
||||||
|
promoCode: promoCode,
|
||||||
|
order: {
|
||||||
|
appointmentType: order.serviceLocation.appointmentType,
|
||||||
|
carId: order.vehicle.carId,
|
||||||
|
correlationId: order.referralCorrelationId,
|
||||||
|
eon: order.eon,
|
||||||
|
isRepair: order.damage.isRepair,
|
||||||
|
glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
|
||||||
|
lineItemsOnOrder: getArrayOfAllLineItems(order.lineItems),
|
||||||
|
addableVaps: addableVaps,
|
||||||
|
parentAccountNumber: order.payment.parentAccountNumber,
|
||||||
|
referralSequenceNumber: order.referralSequenceNumber,
|
||||||
|
serviceState: order.serviceLocation.state,
|
||||||
|
serverData: order.lineItems.serverData,
|
||||||
|
vehicleYear: "" + order.vehicle.year,
|
||||||
|
zipCodeOrProviderCtu:
|
||||||
|
order.serviceLocation.appointmentType === "Inshop"
|
||||||
|
? order.serviceLocation.provider.address.zipCodeCtu
|
||||||
|
: order.serviceLocation.zipCodeCtu,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateResponse = await globalMethods.callHttpClient({
|
||||||
|
method: endpoints.ValidatePromo.method,
|
||||||
|
endpoint: endpoints.ValidatePromo.url,
|
||||||
|
payload: requestObject,
|
||||||
|
logApiCall: true,
|
||||||
|
pageNameToLog: pageNameToLog,
|
||||||
|
});
|
||||||
|
|
||||||
|
context.commit(
|
||||||
|
storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA,
|
||||||
|
validateResponse.data.lineItemsServerData
|
||||||
|
);
|
||||||
|
|
||||||
|
return validateResponse;
|
||||||
|
},
|
||||||
|
async revalidateOrderPromosAndUpdateStore(context, { pageNameToLog }) {
|
||||||
|
const order = context.getters.order;
|
||||||
|
|
||||||
|
let requestObject = {
|
||||||
|
inactivePromos: order.payment.inactivePromos,
|
||||||
|
order: {
|
||||||
|
appointmentType: order.serviceLocation.appointmentType,
|
||||||
|
carId: order.vehicle.carId,
|
||||||
|
correlationId: order.referralCorrelationId,
|
||||||
|
eon: order.eon,
|
||||||
|
isRepair: order.damage.isRepair,
|
||||||
|
glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
|
||||||
|
lineItemsOnOrder: getArrayOfAllLineItems(order.lineItems),
|
||||||
|
parentAccountNumber: order.payment.parentAccountNumber,
|
||||||
|
referralSequenceNumber: order.referralSequenceNumber,
|
||||||
|
serviceState: order.serviceLocation.state,
|
||||||
|
serverData: order.lineItems.serverData,
|
||||||
|
vehicleYear: "" + order.vehicle.year,
|
||||||
|
zipCodeOrProviderCtu:
|
||||||
|
order.serviceLocation.appointmentType === "Inshop"
|
||||||
|
? order.serviceLocation.provider.address.zipCodeCtu
|
||||||
|
: order.serviceLocation.zipCodeCtu,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const revalidateResponse = await globalMethods.callHttpClient({
|
||||||
|
method: endpoints.RevalidatePromos.method,
|
||||||
|
endpoint: endpoints.RevalidatePromos.url,
|
||||||
|
payload: requestObject,
|
||||||
|
logApiCall: true,
|
||||||
|
pageNameToLog: pageNameToLog,
|
||||||
|
});
|
||||||
|
|
||||||
|
const revalidationErrorPromoCodes = revalidateResponse.data.errors.map((x) => x.promoCode);
|
||||||
|
|
||||||
|
context.commit(storeMutations.UPDATE_PROMOS, revalidateResponse.data.promoLineItems);
|
||||||
|
context.commit(storeMutations.UPDATE_INACTIVE_PROMOS, revalidationErrorPromoCodes);
|
||||||
|
context.commit(
|
||||||
|
storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA,
|
||||||
|
revalidateResponse.data.lineItemsServerData
|
||||||
|
);
|
||||||
|
|
||||||
|
return revalidateResponse;
|
||||||
|
},
|
||||||
|
|
||||||
// Misc order actions
|
// Misc order actions
|
||||||
saveSchedule(context, scheduleInfo) {
|
saveSchedule(context, scheduleInfo) {
|
||||||
context.commit(storeMutations.UPDATE_SCHEDULE, scheduleInfo);
|
context.commit(storeMutations.UPDATE_SCHEDULE, scheduleInfo);
|
||||||
|
|
@ -2203,7 +2315,7 @@ export const actions = {
|
||||||
if (!deepEqual(parts, context.state.order.lineItems.glassParts)) {
|
if (!deepEqual(parts, context.state.order.lineItems.glassParts)) {
|
||||||
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
}
|
}
|
||||||
|
addGuidToLineItemsIfNotAlreadyThere(parts);
|
||||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
|
context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -2380,6 +2492,19 @@ function addTaxesToPricedLineItems(lineItems, taxingLineItems) {
|
||||||
return lineItems;
|
return lineItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getArrayOfAllLineItems(lineItems) {
|
||||||
|
let consolidatedLineItemsArray = [];
|
||||||
|
if (lineItems.glassParts != null)
|
||||||
|
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.glassParts];
|
||||||
|
if (lineItems.supportingItems != null)
|
||||||
|
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.supportingItems];
|
||||||
|
if (lineItems.vaps != null)
|
||||||
|
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.vaps];
|
||||||
|
if (lineItems.promos != null)
|
||||||
|
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.promos];
|
||||||
|
return consolidatedLineItemsArray;
|
||||||
|
}
|
||||||
|
|
||||||
function getFlattenedArrayOfLineItemsWithChildParts(lineItems) {
|
function getFlattenedArrayOfLineItemsWithChildParts(lineItems) {
|
||||||
let flattenedArray = [];
|
let flattenedArray = [];
|
||||||
lineItems?.forEach((lineItem) => {
|
lineItems?.forEach((lineItem) => {
|
||||||
|
|
@ -2414,6 +2539,37 @@ function convertGlassPieceToBackEndCompatibleFormat(glassPieces) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renameGlassToReplaceAttributes(glassToReplace) {
|
||||||
|
let newGlassToReplace = [];
|
||||||
|
if (glassToReplace) {
|
||||||
|
glassToReplace.forEach((item) => {
|
||||||
|
newGlassToReplace.push({ location: item.glassLocation, name: item.glassName });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return newGlassToReplace;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGuidToLineItemsIfNotAlreadyThere(lineItems) {
|
||||||
|
lineItems.forEach((lineItem) => {
|
||||||
|
if (!lineItem.id) {
|
||||||
|
lineItem.id = crypto.randomUUID();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncLineItemIds(lineItemsWithoutIds, lineItemsWithIds) {
|
||||||
|
if (!lineItemsWithIds || !lineItemsWithoutIds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lineItemsWithoutIds.forEach((noId) => {
|
||||||
|
lineItemsWithIds.forEach((withId) => {
|
||||||
|
if (noId.partNumber === withId.partNumber && noId.partType === withId.partType) {
|
||||||
|
noId.id = withId.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function providersEqual(providerA, providerB) {
|
function providersEqual(providerA, providerB) {
|
||||||
return (
|
return (
|
||||||
providerA.providerNumber === providerB.providerNumber &&
|
providerA.providerNumber === providerB.providerNumber &&
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
||||||
// Mock global method
|
// Mock global method
|
||||||
globalMethods.callHttpClient = jest.fn();
|
globalMethods.callHttpClient = jest.fn();
|
||||||
|
|
||||||
|
global.crypto = { randomUUID: jest.fn() };
|
||||||
|
|
||||||
describe("Mutations", () => {
|
describe("Mutations", () => {
|
||||||
it("Updates vehicle year in state", () => {
|
it("Updates vehicle year in state", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -1378,6 +1380,8 @@ describe("Actions", () => {
|
||||||
state: state,
|
state: state,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
context.state.order.lineItems = [];
|
||||||
|
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
const dispatch = jest.fn();
|
const dispatch = jest.fn();
|
||||||
|
|
||||||
|
|
@ -1385,10 +1389,10 @@ describe("Actions", () => {
|
||||||
context.dispatch = dispatch;
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
actions.saveGlassParts(context, { glassParts: {} });
|
actions.saveGlassParts(context, []);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, { glassParts: {} });
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, []);
|
||||||
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1417,7 +1421,7 @@ describe("Actions", () => {
|
||||||
state: {
|
state: {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
supportingItems: ["TestValue1", "TestValue2"],
|
supportingItems: [{ partNum: "TestValue1" }, { partNum: "TestValue2" }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1430,7 +1434,11 @@ describe("Actions", () => {
|
||||||
context.dispatch = dispatch;
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
actions.saveSupportingItems(context, ["TestValue3", "TestValue4", "TestValue5"]);
|
actions.saveSupportingItems(context, [
|
||||||
|
{ partNum: "TestValue3" },
|
||||||
|
{ partNum: "TestValue4" },
|
||||||
|
{ partNum: "TestValue5" },
|
||||||
|
]);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
expect(dispatch).toBeCalledWith(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
|
||||||
|
|
@ -1442,7 +1450,7 @@ describe("Actions", () => {
|
||||||
state: {
|
state: {
|
||||||
order: {
|
order: {
|
||||||
lineItems: {
|
lineItems: {
|
||||||
supportingItems: ["TestValue1", "TestValue2"],
|
supportingItems: [{ partNum: "TestValue1" }, { partNum: "TestValue2" }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1455,7 +1463,10 @@ describe("Actions", () => {
|
||||||
context.dispatch = dispatch;
|
context.dispatch = dispatch;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
actions.saveSupportingItems(context, ["TestValue1", "TestValue2"]);
|
actions.saveSupportingItems(context, [
|
||||||
|
{ partNum: "TestValue1" },
|
||||||
|
{ partNum: "TestValue2" },
|
||||||
|
]);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(dispatch).not.toBeCalledWith(
|
expect(dispatch).not.toBeCalledWith(
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,24 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
// Set max-width on columns to prevent overly-wide
|
||||||
|
// components on extra wide screens.
|
||||||
|
.col-md-6 {
|
||||||
|
max-width: 472px;
|
||||||
|
@include media-breakpoint-up(xl) {
|
||||||
|
max-width: 708px;
|
||||||
|
}
|
||||||
|
.col {
|
||||||
|
max-width: 236px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.col-xl-4 {
|
||||||
|
max-width: 472px;
|
||||||
|
.col {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//END set max-width on columns
|
||||||
}
|
}
|
||||||
.pointer {
|
.pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue