Merge branch 'develop' into feature/Digital/CSR-1629
This commit is contained in:
commit
e162a5fd0e
53 changed files with 826 additions and 312 deletions
|
|
@ -143,6 +143,7 @@ body .buttonContainer button {
|
|||
@media (hover: hover) {
|
||||
body .buttonContainer button {
|
||||
background: linear-gradient(270deg, #1574a1 0%, #003d58 100%);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
body .buttonContainer button:focus {
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ body {
|
|||
height: 48px;
|
||||
@media (hover: hover) {
|
||||
background: linear-gradient(270deg, #1574a1 0%, #003d58 100%);
|
||||
cursor: pointer;
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 3px, 0 0 0 5.5px #06577c;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ const applicationConfig = {
|
|||
"//" +
|
||||
location.host +
|
||||
"/fmg/?fmgPage=payment-method&src=concept-funnel",
|
||||
PIA_ERROR_URL:
|
||||
location.protocol +
|
||||
"//" +
|
||||
location.host +
|
||||
"/fmg/?fmgPage=payment-pia-return&src=concept-funnel",
|
||||
CONFIRMATION_URL: location.protocol + "//" + location.host + "/fmg/?fmgPage=confirmation",
|
||||
GOOGLE_CALENDAR: "https://www.google.com/calendar/render?action=TEMPLATE",
|
||||
YAHOO_CALENDAR: "https://calendar.yahoo.com/?v=60",
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ const endpoints = {
|
|||
},
|
||||
PriceOrderItems: {
|
||||
url: "/price/api/v1/price/order-items",
|
||||
method: "GET",
|
||||
method: "POST",
|
||||
},
|
||||
ValidatePromo: {
|
||||
url: "/price/api/v1/price/order-promo",
|
||||
|
|
@ -172,6 +172,10 @@ const endpoints = {
|
|||
url: "/account/api/v1/account/insurance-companies",
|
||||
method: "GET",
|
||||
},
|
||||
GetBillToAccountNumber: {
|
||||
url: "/account/api/v1/account/bill-to-account-number",
|
||||
method: "GET",
|
||||
},
|
||||
};
|
||||
|
||||
export { endpoints };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const experimentUniverses = {
|
||||
CONCEPT_FUNNEL: "ConceptFunnel",
|
||||
RECAL_PRICE_REMOVAL: "NextGen_RecalPriceRemoval",
|
||||
};
|
||||
|
||||
const experimentSettings = {
|
||||
|
|
@ -11,6 +12,7 @@ const experimentSettings = {
|
|||
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
|
||||
IS_EMAIL_OPTIONAL: "isEmailOptional",
|
||||
SERVICE_PACKAGE_DISCOUNT: "OfferServicePackageDiscount",
|
||||
RECAL_PRICE_REMOVE: "RecalPriceRemove",
|
||||
};
|
||||
|
||||
const experimentTriggers = {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
export const headerKeys = {
|
||||
EXPERIMENT: "X-Experiment-Data",
|
||||
APPLICATION_NAME: "X-Application-Name",
|
||||
REFERRAL_SEQUENCE_NUMBER: "X-Referral-Sequence-Number",
|
||||
SESSION_SEQUENCE_NUMBER: "X-Session-Sequence-Number",
|
||||
TRANSACTION_ID: "X-Transaction-Id",
|
||||
EON: "X-EON",
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ const queryStrings = {
|
|||
VIN_SELECTION: "vinselection",
|
||||
SERVICE_PACKAGE: "servicepackage",
|
||||
NUMBER_OF_CHIPS: "numberofchips",
|
||||
LOG: "log",
|
||||
UTM_SOURCE: "_source",
|
||||
UTM_MEDIUM: "_medium",
|
||||
UTM_CAMPAIGN: "_campaign",
|
||||
|
|
@ -43,6 +44,7 @@ const queryStrings = {
|
|||
CJUNCTION: "cjunction",
|
||||
ORGANIC: "organic",
|
||||
ORGANIC_SOCIAL: "organic_social",
|
||||
EXPERIMENTS: "experiments",
|
||||
};
|
||||
|
||||
export { queryStrings };
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ const storeActions = {
|
|||
VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA: "validateOrderPromoAndSaveServerData",
|
||||
REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA: "revalidateOrderPromosAndSaveServerData",
|
||||
GET_INSURANCE_COMPANY_LIST: "getInsuranceCompanyList",
|
||||
GET_BILL_TO_ACCOUNT_NUMBER: "getBillToAccountNumber",
|
||||
|
||||
// DEPENDENCY MUTATIONS
|
||||
RESET_DAMAGE_STATE_AND_DEPENDENCIES: "resetDamageAndDependencies",
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ const storeMutations = {
|
|||
UPDATE_EXTERNAL_PARAMETER_IS_INSURANCE: "updateExternalParameterIsInsurance",
|
||||
UPDATE_EXTERNAL_PARAMETER_VIN_SELECTION: "updateExternalParameterVinSelection",
|
||||
UPDATE_EXTERNAL_PARAMETER_SERVICE_PACKAGE: "updateExternalParameterServicePackage",
|
||||
UPDATE_EXTERNAL_PARAMETER_SOURCE: "updateExternalParameterSource",
|
||||
|
||||
//RESET EXTERNAL_PARAMETER MUTATIONS
|
||||
RESET_EXTERNAL_PARAMETER_VEHICLE_STATE: "resetExternalParameterVehicleState",
|
||||
|
|
|
|||
|
|
@ -1123,42 +1123,6 @@ describe("baseInputButton.vue", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("buttonId", () => {
|
||||
test("groupName and value combo yield correct id for input button with string value", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
groupName: "my test-name",
|
||||
value: "Aaa-BBB CcC",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.buttonId).toBe("my-test-name-Aaa-BBB-CcC");
|
||||
expect(inputElement.attributes().id).toBe("my-test-name-Aaa-BBB-CcC");
|
||||
});
|
||||
|
||||
test("groupName and value combo yield correct id for input button with number value", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
groupName: "my test-name",
|
||||
value: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.buttonId).toBe("my-test-name-2");
|
||||
expect(inputElement.attributes().id).toBe("my-test-name-2");
|
||||
});
|
||||
});
|
||||
|
||||
describe("inputType", () => {
|
||||
test("isMultiSelect is true => inputType is 'checkbox'", () => {
|
||||
// Arrange/Act
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import {
|
|||
handleInputComponentBlur,
|
||||
} from "@/helpers/button-question-focus-helper";
|
||||
import { inputButtonProps } from "@/digital-components/base-input-button/button-functionality-props";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export default {
|
||||
name: "base-input-button",
|
||||
|
|
@ -45,6 +46,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
valueToEmit: null,
|
||||
buttonId: uuidv4(),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -153,11 +155,6 @@ export default {
|
|||
inputType() {
|
||||
return this.isMultiSelect ? "checkbox" : "radio";
|
||||
},
|
||||
buttonId() {
|
||||
return `${this.groupName?.replace(" ", "-")}-${this.value
|
||||
?.toString()
|
||||
?.replace(" ", "-")}`;
|
||||
},
|
||||
isValueSelectedOnClick() {
|
||||
return this.isMultiSelect || this.selectingInitiatesLoad;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
<template>
|
||||
<div class="cart">
|
||||
<div class="px-0">
|
||||
<!-- set class to 'expaned' on line 7 so cart is open on page load. This may be temporary -->
|
||||
<div
|
||||
class="row vin-toggle flex align-items-center pt-4"
|
||||
:class="[isExpanded ? '' : 'expanded']"
|
||||
:class="[isExpanded ? 'expanded' : '']"
|
||||
@click="toggleIsExpanded()">
|
||||
<a
|
||||
aria-label="expand cart"
|
||||
|
|
@ -140,7 +139,7 @@
|
|||
</div>
|
||||
<div
|
||||
class="my-2 lh-1 applied-promo-tag"
|
||||
v-for="(promoCode, i) in this.getPromoCodeList()"
|
||||
v-for="(promoCode, i) in getPromoCodeList"
|
||||
:key="i">
|
||||
<span class="caption"
|
||||
>Promo code <span class="promo-code">{{ promoCode }}</span> applied
|
||||
|
|
@ -184,13 +183,15 @@ export default {
|
|||
allowItemRemoval: Boolean,
|
||||
recyclingModalCmsWidgetName: String,
|
||||
showAsPaid: Boolean,
|
||||
isInsurance: Boolean,
|
||||
insuranceDeductible: Number,
|
||||
insuranceCompanyName: String,
|
||||
showInsuranceCoverageAs: String,
|
||||
shouldHideRecalibration: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false,
|
||||
isExpanded: true, // set default to true so cart is open on page load. This may be temporary.
|
||||
currencyFormatter: new Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
|
|
@ -283,6 +284,7 @@ export default {
|
|||
(lineItemsToKeep) => lineItemsToKeep.cartItemType != cartItemType
|
||||
);
|
||||
},
|
||||
|
||||
getPromoCodeList() {
|
||||
if (this.$refs["promoModalQuestion"]) {
|
||||
return this.$refs["promoModalQuestion"].getPromoCodeList();
|
||||
|
|
@ -299,6 +301,15 @@ export default {
|
|||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
lineItemsWithoutRecal() {
|
||||
if (!this.lineItems || this.lineItems.length < 1) return;
|
||||
|
||||
const lineItemsCopy = deepClone(this.lineItems);
|
||||
lineItemsCopy.supportingItems = lineItemsCopy.supportingItems
|
||||
? baseMixin.methods.filterOutRecalibration(lineItemsCopy?.supportingItems)
|
||||
: [];
|
||||
return lineItemsCopy;
|
||||
},
|
||||
showCoverageAsPending() {
|
||||
return this.showInsuranceCoverageAs === coverageStatus.PENDING;
|
||||
},
|
||||
|
|
@ -427,7 +438,15 @@ export default {
|
|||
packagePrice() {
|
||||
let packagePrice = 0;
|
||||
|
||||
if (!this.showCoverageAsVerified && !this.showCoverageAsPending) {
|
||||
if (!this.isInsurance && this.shouldHideRecalibration) {
|
||||
// Update packagePrice if Cash only && is part of RemoveRecal experiment
|
||||
packagePrice = baseMixin.methods.getTierOnePackagePrice(
|
||||
baseMixin.methods.filterOutFees(
|
||||
baseMixin.methods.filterOutRecalibration(this.availableLineItems) // Strip out recal before filtering out fees
|
||||
)
|
||||
);
|
||||
} else if (!this.showCoverageAsVerified && !this.showCoverageAsPending) {
|
||||
// Update packagePrice if is verified insurance OR if Cash && not part of RemoveRecal experiment
|
||||
packagePrice = baseMixin.methods.getTierOnePackagePrice(
|
||||
baseMixin.methods.filterOutFees(this.availableLineItems)
|
||||
);
|
||||
|
|
@ -916,8 +935,6 @@ export default {
|
|||
promoCartItems() {
|
||||
const promoCartItems = [];
|
||||
|
||||
// clone the promos array
|
||||
const promosClone = deepClone(this.promo);
|
||||
// get unique promo codes
|
||||
const uniquePromoCodes = [
|
||||
...new Set(
|
||||
|
|
@ -983,16 +1000,23 @@ export default {
|
|||
return this.getCmsContent("SubtotalTextWidget", "Text");
|
||||
},
|
||||
subTotal() {
|
||||
return baseMixin.methods.getSubTotal(this.lineItems);
|
||||
if (!this.lineItems || this.lineItems.length < 1) return;
|
||||
return this.isInsurance || !this.shouldHideRecalibration
|
||||
? baseMixin.methods.getSubTotal(this.lineItems) // calculate with recal (if on order)
|
||||
: baseMixin.methods.getSubTotal(this.lineItemsWithoutRecal); // calculate without recal
|
||||
},
|
||||
salesTax() {
|
||||
return baseMixin.methods.getSalesTax(this.lineItems);
|
||||
if (!this.lineItems || this.lineItems.length < 1) return;
|
||||
return this.isInsurance || !this.shouldHideRecalibration
|
||||
? baseMixin.methods.getSalesTax(this.lineItems) // calculate with recal (if on order)
|
||||
: baseMixin.methods.getSalesTax(this.lineItemsWithoutRecal); // calculated without recal
|
||||
},
|
||||
amountDue() {
|
||||
if (this.showAsPaid) {
|
||||
return 0;
|
||||
}
|
||||
return baseMixin.methods.getAmountDue(this.lineItems);
|
||||
if (!this.lineItems || this.lineItems.length < 1) return;
|
||||
if (this.showAsPaid) return 0;
|
||||
return this.isInsurance || !this.shouldHideRecalibration
|
||||
? baseMixin.methods.getAmountDue(this.lineItems) // calculate with recal (if on order)
|
||||
: baseMixin.methods.getAmountDue(this.lineItemsWithoutRecal); // calculated without recal
|
||||
},
|
||||
amountPaid() {
|
||||
if (!this.showAsPaid) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import axios from "axios";
|
||||
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import router from "@/router";
|
||||
import store from "@/store";
|
||||
|
||||
|
|
@ -57,9 +58,17 @@ export default {
|
|||
}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO;
|
||||
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
||||
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
|
||||
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
|
||||
const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" });
|
||||
const headers = {
|
||||
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
||||
[headerKeys.APPLICATION_NAME]: applicationConfig.APPLICATION_NAME,
|
||||
[headerKeys.SESSION_SEQUENCE_NUMBER]: store.getters.applicationUser?.savedSessionId,
|
||||
[headerKeys.REFERRAL_SEQUENCE_NUMBER]: order?.referralSequenceNumber,
|
||||
[headerKeys.TRANSACTION_ID]: crypto.randomUUID(),
|
||||
[headerKeys.EON]: order?.eon,
|
||||
};
|
||||
|
||||
axios({
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ global.$logger = {
|
|||
logError: jest.fn(),
|
||||
logCritical: jest.fn(),
|
||||
};
|
||||
global.crypto = { randomUUID: jest.fn() };
|
||||
|
||||
it("Global Methods - Call Http Client - Should Resolve Promise", () => {
|
||||
//Arrange
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ export function getDateDifferenceInDays(startDate, endDate) {
|
|||
return Math.round(difference / (1000 * 3600 * 24));
|
||||
}
|
||||
export function get12HourTimeFormat(time) {
|
||||
if (!time) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check correct time format and split into components
|
||||
time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time];
|
||||
|
||||
|
|
@ -20,8 +24,12 @@ export function get12HourTimeFormat(time) {
|
|||
return time.join(""); // return adjusted time or original string
|
||||
}
|
||||
export function get12HourTimeMobileFormat(time) {
|
||||
if (!time) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
if (time.length > 1) {
|
||||
// If time format correct
|
||||
|
|
|
|||
|
|
@ -107,8 +107,12 @@ export async function getImplicitNavigation(toRoute) {
|
|||
*/
|
||||
|
||||
export async function navigateToHeritageFunnel({ shouldSaveSession, pageNameToLog }) {
|
||||
console.log(new Date() + " navigateToHeritageFunnel: " + JSON.stringify(applicationConfig));
|
||||
console.log(new Date() + " navigateToHeritageFunnel: " + applicationConfig.HERITAGE_FUNNEL);
|
||||
const log = getQuerystringParameter(queryStrings.LOG);
|
||||
if (eval(log)) {
|
||||
console.log(new Date() + " applicationConfig: " + JSON.stringify(applicationConfig));
|
||||
console.log(new Date() + " navigateToHeritageFunnel: " + applicationConfig.HERITAGE_FUNNEL);
|
||||
}
|
||||
|
||||
// Create the order (or save existing order) when navigating to Heritage Funnel.
|
||||
if (shouldSaveSession) {
|
||||
await saveSession({ pageNameToLog: pageNameToLog, shouldAwaitSaveSessionQueue: true });
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ import {
|
|||
import store from "@/store";
|
||||
import { serviceType } from "@/constants/service-type";
|
||||
import { applicationConfig } from "@/constants/application-config.js";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
|
||||
export default {
|
||||
name: "add-to-calendar",
|
||||
components: {
|
||||
|
|
@ -110,10 +112,10 @@ export default {
|
|||
return this.getCmsContent(this.sameDayDropOffWidgetName, "BodyText");
|
||||
},
|
||||
UniqueId() {
|
||||
return store.getters.submittedOrder.referralNumber?.toString();
|
||||
return this.getSubmittedOrder()?.referralNumber?.toString();
|
||||
},
|
||||
ServiceType() {
|
||||
const isRepair = store.getters.submittedOrder.damage.isRepair;
|
||||
const isRepair = this.getSubmittedOrder()?.damage.isRepair;
|
||||
const funnelHasRecalibrationPart = store.getters.isRecalibrationOnSubmittedOrder;
|
||||
if (!isRepair) {
|
||||
if (funnelHasRecalibrationPart) {
|
||||
|
|
@ -125,7 +127,7 @@ export default {
|
|||
return serviceType.REPAIR;
|
||||
},
|
||||
RouteCode() {
|
||||
return store.getters.submittedOrder.schedule.routeCode;
|
||||
return this.getSubmittedOrder()?.schedule.routeCode;
|
||||
},
|
||||
Appointment() {
|
||||
var subject = "";
|
||||
|
|
@ -206,6 +208,9 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
getSubmittedOrder() {
|
||||
return baseMixin.methods.getSubmittedOrder();
|
||||
},
|
||||
setCalendarAppointment(calendarOption) {
|
||||
if (
|
||||
calendarOption.name == calendarOptions.ICAL ||
|
||||
|
|
|
|||
|
|
@ -19,67 +19,6 @@ beforeEach(() => {
|
|||
jest.restoreAllMocks();
|
||||
jest.clearAllMocks();
|
||||
store.getters = {
|
||||
submittedOrder: {
|
||||
schedule: {
|
||||
date: "2023-01-01",
|
||||
startTime: "09:00",
|
||||
endTime: "10:00",
|
||||
routeCode: "000",
|
||||
jobMaxMinutes: "120",
|
||||
jobMinMinutes: "60",
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: [
|
||||
{
|
||||
partNumber: "ABC123",
|
||||
},
|
||||
],
|
||||
supportingItems: [],
|
||||
},
|
||||
serviceLocation: {
|
||||
address: "test",
|
||||
address2: "123",
|
||||
city: "test",
|
||||
state: "AZ",
|
||||
appointmentType: "Inshop",
|
||||
zipCode: "12345",
|
||||
zipCodeCtu: "01234",
|
||||
provider: {
|
||||
providerNumber: "123",
|
||||
address: {
|
||||
streetAddress: "test1",
|
||||
city: "test",
|
||||
state: "AZ",
|
||||
zipCode: "12345",
|
||||
zipCodeCtu: "01234",
|
||||
},
|
||||
},
|
||||
},
|
||||
damage: {
|
||||
isRepair: false,
|
||||
},
|
||||
payment: {
|
||||
isPia: true,
|
||||
isInsurance: false,
|
||||
insuranceCoverage: {
|
||||
coverageStatus: "test",
|
||||
},
|
||||
},
|
||||
policy: {
|
||||
isNoComp: false,
|
||||
currentDeductible: 1,
|
||||
insuranceCompanyName: "test name",
|
||||
},
|
||||
referralNumber: "1234567",
|
||||
vehicle: {
|
||||
year: "2004",
|
||||
make: "Ford",
|
||||
model: "F Series F250",
|
||||
},
|
||||
customer: {
|
||||
emailAddress: "test@test.com",
|
||||
},
|
||||
},
|
||||
order: {
|
||||
schedule: {
|
||||
date: "2023-01-01",
|
||||
|
|
@ -96,6 +35,7 @@ beforeEach(() => {
|
|||
supportingItems: [],
|
||||
},
|
||||
serviceLocation: {
|
||||
appointmentType: AppointmentTypeStrings.IN_SHOP,
|
||||
address: "test",
|
||||
address2: "123",
|
||||
city: "test",
|
||||
|
|
@ -180,6 +120,20 @@ describe("computed properties...", () => {
|
|||
test("ScheduleDateFormatted should return date in expected format.", () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
startTime: "09:00",
|
||||
endTime: "11:00",
|
||||
date: "2023-01-01",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleDateFormatted;
|
||||
|
|
@ -189,11 +143,22 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("ScheduleTimeFormatted should return mobile time in expected format.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.serviceLocation.appointmentType =
|
||||
AppointmentTypeStrings.MOBILE;
|
||||
store.getters.submittedOrder.schedule.startTime = "09:00";
|
||||
store.getters.submittedOrder.schedule.endTime = "11:00";
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
startTime: "09:00",
|
||||
endTime: "11:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
wrapper.vm.submittedOrder.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleTimeFormatted;
|
||||
|
|
@ -203,11 +168,22 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("ScheduleTimeFormatted should return DROP_OFF time in expected format.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.serviceLocation.appointmentType =
|
||||
AppointmentTypeStrings.DROP_OFF;
|
||||
store.getters.submittedOrder.schedule.startTime = "09:00";
|
||||
store.getters.submittedOrder.schedule.endTime = "11:00";
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
startTime: "09:00",
|
||||
endTime: "11:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
wrapper.vm.submittedOrder.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleTimeFormatted;
|
||||
|
|
@ -217,11 +193,22 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("ScheduleTimeFormatted should return Inshop time in expected format.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.serviceLocation.appointmentType =
|
||||
AppointmentTypeStrings.IN_SHOP;
|
||||
store.getters.submittedOrder.schedule.startTime = "09:00";
|
||||
store.getters.submittedOrder.schedule.endTime = "11:00";
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
startTime: "09:00",
|
||||
endTime: "11:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
wrapper.vm.submittedOrder.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleTimeFormatted;
|
||||
|
|
@ -231,10 +218,21 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("AppointmentWordingText should return mobile text in expected format.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.serviceLocation.appointmentType =
|
||||
AppointmentTypeStrings.MOBILE;
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
endTime: "10:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
wrapper.vm.submittedOrder.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentWordingText;
|
||||
|
||||
|
|
@ -243,10 +241,21 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("AppointmentWordingText should return DROP_OFF text in expected format.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.serviceLocation.appointmentType =
|
||||
AppointmentTypeStrings.DROP_OFF;
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
endTime: "10:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
wrapper.vm.submittedOrder.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentWordingText;
|
||||
|
|
@ -256,10 +265,20 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("AppointmentWordingText should return IN_SHOP text in expected format.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.serviceLocation.appointmentType =
|
||||
AppointmentTypeStrings.IN_SHOP;
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
endTime: "10:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentWordingText;
|
||||
|
|
@ -280,6 +299,19 @@ describe("computed properties...", () => {
|
|||
test("ScheduleDate should return date in expected format.", () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2023-01-01",
|
||||
endTime: "10:00",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleDate;
|
||||
|
|
@ -290,6 +322,19 @@ describe("computed properties...", () => {
|
|||
test("ScheduleStartTime should return time in expected format.", () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
startTime: "09:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleStartTime;
|
||||
|
|
@ -300,6 +345,19 @@ describe("computed properties...", () => {
|
|||
test("ScheduleEndTime should return time in expected format.", () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
endTime: "10:00",
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ScheduleEndTime;
|
||||
|
|
@ -344,6 +402,18 @@ describe("computed properties...", () => {
|
|||
//Arrange
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ServiceLocationFullAddress;
|
||||
|
|
@ -355,6 +425,18 @@ describe("computed properties...", () => {
|
|||
//Arrange
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
serviceLocation: store.getters.order.serviceLocation,
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.ProviderFullAddress;
|
||||
|
|
@ -366,6 +448,17 @@ describe("computed properties...", () => {
|
|||
//Arrange
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentDuration;
|
||||
|
|
@ -375,9 +468,18 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("Appointment Duration should return with correct duration values in expected format for hours.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.schedule.jobMaxMinutes = 120;
|
||||
store.getters.submittedOrder.schedule.jobMinMinutes = 60;
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 120,
|
||||
jobMinMinutes: 60,
|
||||
routeCode: "",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentDuration;
|
||||
|
|
@ -387,9 +489,18 @@ describe("computed properties...", () => {
|
|||
});
|
||||
test("Appointment Duration should return with correct duration values in expected format for minutes.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.schedule.jobMaxMinutes = 45;
|
||||
store.getters.submittedOrder.schedule.jobMinMinutes = 30;
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 45,
|
||||
jobMinMinutes: 30,
|
||||
routeCode: "",
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentDuration;
|
||||
|
|
@ -400,10 +511,18 @@ describe("computed properties...", () => {
|
|||
|
||||
test("Appointment Duration should return with All Day.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.schedule.jobMaxMinutes = 45;
|
||||
store.getters.submittedOrder.schedule.jobMinMinutes = 30;
|
||||
store.getters.submittedOrder.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF;
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 45,
|
||||
jobMinMinutes: 30,
|
||||
routeCode: RouteCodeFlags.ALL_DAY_DROP_OFF,
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentDuration;
|
||||
|
|
@ -414,10 +533,18 @@ describe("computed properties...", () => {
|
|||
|
||||
test("Appointment Duration should return with Overnight.", () => {
|
||||
//Arrange
|
||||
store.getters.submittedOrder.schedule.jobMaxMinutes = 45;
|
||||
store.getters.submittedOrder.schedule.jobMinMinutes = 30;
|
||||
store.getters.submittedOrder.schedule.routeCode = RouteCodeFlags.OVERNIGHT_DROP_OFF;
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.submittedOrder = {
|
||||
vehicle: {
|
||||
imageUrl: "",
|
||||
},
|
||||
schedule: {
|
||||
date: "2024-09-28",
|
||||
jobMaxMinutes: 45,
|
||||
jobMinMinutes: 30,
|
||||
routeCode: RouteCodeFlags.OVERNIGHT_DROP_OFF,
|
||||
},
|
||||
};
|
||||
|
||||
// Act
|
||||
const testValue = wrapper.vm.AppointmentDuration;
|
||||
|
|
@ -438,6 +565,7 @@ function setupMocks({ customMountOptions }) {
|
|||
getCmsContent: jest.fn().mockImplementation(() => {
|
||||
return wordingText;
|
||||
}),
|
||||
getSettingValue: jest.fn(),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@
|
|||
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||
:insuranceDeductible="currentDeductible"
|
||||
:insuranceCompanyName="insuranceCompanyName"
|
||||
:showInsuranceCoverageAs="showInsuranceCoverageAs" />
|
||||
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
||||
:shouldHideRecalibration="shouldHideRecalibration" />
|
||||
|
||||
<hr class="mb-5" />
|
||||
|
||||
|
|
@ -105,20 +106,23 @@ import { Form } from "vee-validate";
|
|||
import { get12HourTimeFormat, get12HourTimeMobileFormat } from "@/helpers/date-helper";
|
||||
import { coverageStatus } from "@/constants/insurance";
|
||||
import { getDisplayTextForDurationLength } from "@/helpers/duration-length-helper";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
|
||||
export default {
|
||||
name: "confirmation",
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_ORDER);
|
||||
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_ORDER); // This nulls the Store order
|
||||
|
||||
// Call APIs
|
||||
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
|
||||
const wipersPromise = baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_WIPERS,
|
||||
{
|
||||
serviceZipCode: store.getters.submittedOrder.serviceLocation.zipCode,
|
||||
carId: store.getters.submittedOrder.vehicle.carId,
|
||||
serviceZipCode: submittedOrder.serviceLocation.zipCode,
|
||||
carId: submittedOrder.vehicle.carId,
|
||||
},
|
||||
"confirmation"
|
||||
);
|
||||
|
|
@ -147,24 +151,29 @@ export default {
|
|||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const availableVaps = [resultMap.rainDefense, ...resultMap.wipers];
|
||||
const lineItemsFromSubmittedOrder = deepClone(store.getters.submittedOrder.lineItems);
|
||||
const lineItemsFromSubmittedOrder = deepClone(submittedOrder.lineItems);
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.lineItems = lineItemsFromSubmittedOrder;
|
||||
vm.vaps = availableVaps;
|
||||
vm.submittedOrder = submittedOrder;
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lineItems: [],
|
||||
vaps: [],
|
||||
submittedOrder: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
shouldHideRecalibration() {
|
||||
return this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE);
|
||||
},
|
||||
ShowCart() {
|
||||
if (this.isPia && store.getters.submittedOrder.settledTenderAmount == 0) {
|
||||
if (this.isPia && this.submittedOrder?.settledTenderAmount == 0) {
|
||||
// settleTenderAmount always shows 0 via localhost or dev.
|
||||
// Temporarily set return true to see cart in localhost or dev environment
|
||||
return false;
|
||||
|
|
@ -179,62 +188,102 @@ export default {
|
|||
return this.getCmsContent("ScheduleConfirmationWidget", "Image");
|
||||
},
|
||||
CustomerPortalLoginToken() {
|
||||
return store.getters.submittedOrder.customerPortalLoginToken;
|
||||
return this.submittedOrder?.customerPortalLoginToken;
|
||||
},
|
||||
ConfirmationEmailText() {
|
||||
return this.getCmsContent("ConfirmationEmailWidget", "BodyText")
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.customer.emailAddress}",
|
||||
this.submittedOrder?.customer?.emailAddress
|
||||
)
|
||||
?.replaceAll("{custom:MY_ACCOUNT_URL}", applicationConfig.MY_ACCOUNT)
|
||||
?.replaceAll("{custom:CUSTOMER_PORTAL_LOGIN_TOKEN}", this.CustomerPortalLoginToken)
|
||||
?.replaceAll("<", "<")
|
||||
?.replaceAll(">", ">");
|
||||
},
|
||||
ScheduleDate() {
|
||||
return store.getters.submittedOrder.schedule.date;
|
||||
return this.submittedOrder?.schedule?.date;
|
||||
},
|
||||
AppointmentType() {
|
||||
return store.getters.submittedOrder.serviceLocation.appointmentType;
|
||||
return this.submittedOrder?.serviceLocation?.appointmentType;
|
||||
},
|
||||
ScheduleStartTime() {
|
||||
return store.getters.submittedOrder.schedule.startTime;
|
||||
return this.submittedOrder?.schedule?.startTime;
|
||||
},
|
||||
ScheduleEndTime() {
|
||||
return store.getters.submittedOrder.schedule.endTime;
|
||||
return this.submittedOrder?.schedule?.endTime;
|
||||
},
|
||||
InShopWordingText() {
|
||||
return this.getCmsContent("InShopWordingWidget", "BodyText");
|
||||
return this.getCmsContent("InShopWordingWidget", "BodyText")
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.year}",
|
||||
this.submittedOrder?.vehicle?.year
|
||||
)
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.make}",
|
||||
this.submittedOrder?.vehicle?.make
|
||||
)
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.model}",
|
||||
this.submittedOrder?.vehicle?.model
|
||||
);
|
||||
},
|
||||
MobileWordingText() {
|
||||
return this.getCmsContent("MobileWordingWidget", "BodyText");
|
||||
return this.getCmsContent("MobileWordingWidget", "BodyText")
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.year}",
|
||||
this.submittedOrder?.vehicle?.year
|
||||
)
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.make}",
|
||||
this.submittedOrder?.vehicle?.make
|
||||
)
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.model}",
|
||||
this.submittedOrder?.vehicle?.model
|
||||
);
|
||||
},
|
||||
DropOffWordingText() {
|
||||
return this.getCmsContent("DropOffWordingWidget", "BodyText");
|
||||
return this.getCmsContent("DropOffWordingWidget", "BodyText")
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.year}",
|
||||
this.submittedOrder?.vehicle?.year
|
||||
)
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.make}",
|
||||
this.submittedOrder?.vehicle?.make
|
||||
)
|
||||
?.replaceAll(
|
||||
"{custom:submittedOrder.vehicle.model}",
|
||||
this.submittedOrder?.vehicle?.model
|
||||
);
|
||||
},
|
||||
ServiceLocationAddress() {
|
||||
return store.getters.submittedOrder.serviceLocation.address;
|
||||
return this.submittedOrder?.serviceLocation?.address;
|
||||
},
|
||||
ServiceLocationAddress2() {
|
||||
return store.getters.submittedOrder.serviceLocation.address2;
|
||||
return this.submittedOrder?.serviceLocation?.address2;
|
||||
},
|
||||
ServiceLocationCity() {
|
||||
return store.getters.submittedOrder.serviceLocation.city;
|
||||
return this.submittedOrder?.serviceLocation?.city;
|
||||
},
|
||||
ServiceLocationState() {
|
||||
return store.getters.submittedOrder.serviceLocation.state;
|
||||
return this.submittedOrder?.serviceLocation?.state;
|
||||
},
|
||||
ServiceLocationZipCode() {
|
||||
return store.getters.submittedOrder.serviceLocation.zipCode;
|
||||
return this.submittedOrder?.serviceLocation?.zipCode;
|
||||
},
|
||||
ProviderAddress() {
|
||||
return store.getters.submittedOrder.serviceLocation.provider.address.streetAddress;
|
||||
return this.submittedOrder?.serviceLocation?.provider?.address?.streetAddress;
|
||||
},
|
||||
ProviderCity() {
|
||||
return store.getters.submittedOrder.serviceLocation.provider.address.city;
|
||||
return this.submittedOrder?.serviceLocation?.provider?.address?.city;
|
||||
},
|
||||
ProviderState() {
|
||||
return store.getters.submittedOrder.serviceLocation.provider.address.state;
|
||||
return this.submittedOrder?.serviceLocation?.provider?.address?.state;
|
||||
},
|
||||
ProviderZipCode() {
|
||||
return store.getters.submittedOrder.serviceLocation.provider.address.zipCode;
|
||||
return this.submittedOrder?.serviceLocation?.provider?.address?.zipCode;
|
||||
},
|
||||
AppointmentWordingText() {
|
||||
if (this.AppointmentType == AppointmentTypeStrings.MOBILE) {
|
||||
|
|
@ -268,7 +317,7 @@ export default {
|
|||
// This conversion ensures we don't get get GMT induced date changes
|
||||
const dateObject = convertDateStringToDate(this.ScheduleDate);
|
||||
// Ex: Tuesday, April 22
|
||||
return dateObject.toLocaleDateString("en-us", {
|
||||
return dateObject?.toLocaleDateString("en-us", {
|
||||
weekday: "long",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
|
|
@ -286,19 +335,19 @@ export default {
|
|||
}
|
||||
},
|
||||
vehicleBannerImageUrl() {
|
||||
return store.getters.submittedOrder?.vehicle.imageUrl;
|
||||
return this.submittedOrder?.vehicle.imageUrl;
|
||||
},
|
||||
damageInfo() {
|
||||
return store.getters.submittedOrder.damage;
|
||||
return this.submittedOrder?.damage;
|
||||
},
|
||||
isInsurance() {
|
||||
return store.getters.submittedOrder.payment.isInsurance;
|
||||
return this.submittedOrder?.payment?.isInsurance;
|
||||
},
|
||||
isNoComp() {
|
||||
return store.getters.submittedOrder.policy.isNoComp;
|
||||
return this.submittedOrder?.policy?.isNoComp;
|
||||
},
|
||||
isItac() {
|
||||
return store.getters.submittedOrder.policy.isItac;
|
||||
return this.submittedOrder?.policy?.isItac;
|
||||
},
|
||||
hasVapsInCart() {
|
||||
if (this.lineItems?.vaps?.length > 0) {
|
||||
|
|
@ -311,32 +360,28 @@ export default {
|
|||
if (this.isNoComp || this.isItac) {
|
||||
return coverageStatus.NOCOMP;
|
||||
} else {
|
||||
return store.getters.submittedOrder.payment.insuranceCoverage.coverageStatus;
|
||||
return this.submittedOrder?.payment?.insuranceCoverage?.coverageStatus;
|
||||
}
|
||||
},
|
||||
currentDeductible() {
|
||||
return store.getters.submittedOrder.policy.currentDeductible;
|
||||
return this.submittedOrder?.policy?.currentDeductible;
|
||||
},
|
||||
insuranceCompanyName() {
|
||||
return store.getters.submittedOrder.policy.insuranceCompanyName;
|
||||
return this.submittedOrder?.policy?.insuranceCompanyName;
|
||||
},
|
||||
isPia() {
|
||||
return store.getters.submittedOrder?.payment.isPia;
|
||||
return this.submittedOrder?.payment?.isPia;
|
||||
},
|
||||
AppointmentDuration() {
|
||||
const durationMaximum = store.getters.submittedOrder?.schedule?.jobMaxMinutes;
|
||||
const durationMinimum = store.getters.submittedOrder?.schedule?.jobMinMinutes;
|
||||
const durationMaximum = this.submittedOrder?.schedule?.jobMaxMinutes;
|
||||
const durationMinimum = this.submittedOrder?.schedule?.jobMinMinutes;
|
||||
const durationLengthString = "Duration: ";
|
||||
if (
|
||||
store.getters.submittedOrder?.schedule?.routeCode.includes(
|
||||
RouteCodeFlags.ALL_DAY_DROP_OFF
|
||||
)
|
||||
this.submittedOrder?.schedule?.routeCode.includes(RouteCodeFlags.ALL_DAY_DROP_OFF)
|
||||
) {
|
||||
return durationLengthString.concat("All Day");
|
||||
} else if (
|
||||
store.getters.submittedOrder?.schedule?.routeCode.includes(
|
||||
RouteCodeFlags.OVERNIGHT_DROP_OFF
|
||||
)
|
||||
this.submittedOrder?.schedule?.routeCode.includes(RouteCodeFlags.OVERNIGHT_DROP_OFF)
|
||||
) {
|
||||
return durationLengthString.concat("Overnight");
|
||||
} else {
|
||||
|
|
@ -348,7 +393,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
if (store.getters.hasSubmittedOrder) {
|
||||
if (baseMixin.methods.hasSubmittedOrder()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ jest.mock("@/helpers/cms-content-helper", () => ({
|
|||
fetchCmsContentForPage: jest.fn(),
|
||||
}));
|
||||
|
||||
global.crypto = { randomUUID: jest.fn() };
|
||||
describe("estimate.vue", () => {
|
||||
test("Selected vin option is emitted upon selection.", async () => {
|
||||
//Arrange
|
||||
|
|
@ -216,7 +217,7 @@ describe("estimate.vue", () => {
|
|||
describe("estimate.vue", () => {
|
||||
test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => {
|
||||
store.getters = {
|
||||
isExternalParameter: true,
|
||||
externalParameterState: { isExternalParameter: true },
|
||||
externalParameterEstimate: { vinSelection: "decline" },
|
||||
};
|
||||
// Set up the component
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ export default {
|
|||
}
|
||||
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
if (store.getters.externalParameterEstimate.vinSelection) {
|
||||
vm.selectedVinLookupMethod = vinPagesMixin.methods.getVinlookupMethod(
|
||||
store.getters.externalParameterEstimate.vinSelection
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export default {
|
|||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
vm.originalList = resultMap.insuranceCompanyList;
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@
|
|||
pageName="payment-method"
|
||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
||||
recyclingModalCmsWidgetName="RecycleModal"
|
||||
:isInsurance="isInsurance"
|
||||
:insuranceDeductible="currentDeductible"
|
||||
:insuranceCompanyName="insuranceCompanyName"
|
||||
:showInsuranceCoverageAs="showInsuranceCoverageAs" />
|
||||
:showInsuranceCoverageAs="showInsuranceCoverageAs"
|
||||
:shouldHideRecalibration="shouldHideRecalibration" />
|
||||
|
||||
<hr class="my-5" />
|
||||
|
||||
|
|
@ -46,14 +48,16 @@
|
|||
v-bind:isDismissible="false" />
|
||||
</div>
|
||||
|
||||
<div v-if="hasRecal && !isInsurance" class="questions-about-service my-5">
|
||||
<div v-if="!isInsurance" class="questions-about-service my-5">
|
||||
<textBlock
|
||||
cmsWidgetName="QuestionsAboutYourServiceWidget"
|
||||
justifyText="left"
|
||||
class="service-questions" />
|
||||
<textBlock cmsWidgetName="CallOrTextWidget" justifyText="left" />
|
||||
<hr class="my-5" />
|
||||
<div class="d-flex flex-row checkbox-group">
|
||||
<div
|
||||
class="d-flex flex-row checkbox-group"
|
||||
v-if="isRecalibrationOnOrder && shouldHideRecalibration">
|
||||
<checkboxQuestion
|
||||
cmsWidgetName="RecalConfirmWidget"
|
||||
class="mb-5"
|
||||
|
|
@ -124,6 +128,7 @@ import {
|
|||
} from "@/helpers/cms-content-helper";
|
||||
import { paymentMethods } from "@/constants/payment-method-constants";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import {
|
||||
revalidatePromosAndValidateQueryStringPromo,
|
||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||
|
|
@ -147,6 +152,7 @@ import { AppointmentTypeStrings } from "@/constants/schedule-constants";
|
|||
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||
import { mapTaxedLineItemsToStoreFormat } from "../../store";
|
||||
import { coverageStatus } from "@/constants/insurance";
|
||||
import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
|
||||
|
||||
defineRule("payment-method-required", required(errorMessages.OPTION_REQUIRED));
|
||||
defineRule("recal-ack-required", required(errorMessages.RECAL_ACK_REQUIRED));
|
||||
|
|
@ -571,22 +577,24 @@ export default {
|
|||
);
|
||||
},
|
||||
hasSubmittedOrder() {
|
||||
return this.$store.getters.hasSubmittedOrder;
|
||||
return baseMixin.methods.hasSubmittedOrder();
|
||||
},
|
||||
getSubmittedOrder() {
|
||||
return baseMixin.methods.getSubmittedOrder();
|
||||
},
|
||||
openModal(modalName) {
|
||||
this.$refs[modalName].openModal();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasRecal() {
|
||||
const recalLineItem = this.$store.getters.order.lineItems?.supportingItems?.find(
|
||||
(lineItem) => lineItem.partType.indexOf(partTypeStrings.RECALIBRATION) !== -1
|
||||
isRecalibrationOnOrder() {
|
||||
return containsLineItemWithPartType(
|
||||
partTypeStrings.RECALIBRATION,
|
||||
this.$store.getters.order.lineItems?.supportingItems
|
||||
);
|
||||
|
||||
if (recalLineItem) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
shouldHideRecalibration() {
|
||||
return this.getSettingValue(experimentSettings.RECAL_PRICE_REMOVE);
|
||||
},
|
||||
showApplePay() {
|
||||
return baseMixin.methods.showApplePay();
|
||||
|
|
@ -596,17 +604,17 @@ export default {
|
|||
},
|
||||
isInsurance() {
|
||||
return this.hasSubmittedOrder()
|
||||
? this.$store.getters.submittedOrder.payment.isInsurance
|
||||
? this.getSubmittedOrder()?.payment.isInsurance
|
||||
: this.$store.getters.payment.isInsurance;
|
||||
},
|
||||
isNoComp() {
|
||||
return this.hasSubmittedOrder()
|
||||
? this.$store.getters.submittedOrder.policy.isNoComp
|
||||
? this.getSubmittedOrder()?.policy.isNoComp
|
||||
: this.$store.getters.policy.isNoComp;
|
||||
},
|
||||
isItac() {
|
||||
return this.hasSubmittedOrder()
|
||||
? this.$store.getters.submittedOrder.policy.isItac
|
||||
? this.getSubmittedOrder()?.policy.isItac
|
||||
: this.$store.getters.policy.isItac;
|
||||
},
|
||||
hasVapsInCart() {
|
||||
|
|
@ -621,13 +629,13 @@ export default {
|
|||
return coverageStatus.NOCOMP;
|
||||
} else {
|
||||
return this.hasSubmittedOrder()
|
||||
? this.$store.getters.submittedOrder.payment.insuranceCoverage.coverageStatus
|
||||
? this.getSubmittedOrder()?.payment.insuranceCoverage.coverageStatus
|
||||
: this.$store.getters.payment.insuranceCoverage.coverageStatus;
|
||||
}
|
||||
},
|
||||
currentDeductible() {
|
||||
return this.hasSubmittedOrder()
|
||||
? this.$store.getters.submittedOrder.policy.currentDeductible
|
||||
? this.getSubmittedOrder()?.policy.currentDeductible
|
||||
: this.$store.getters.policy.currentDeductible;
|
||||
},
|
||||
insuranceCompanyName() {
|
||||
|
|
@ -658,7 +666,7 @@ export default {
|
|||
return true;
|
||||
}
|
||||
} else {
|
||||
if (this.isPiaEnabled && this.totalAmountDue > 0 && !this.hasRecal) {
|
||||
if (this.isPiaEnabled && this.totalAmountDue > 0 && !this.shouldHideRecalibration) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -764,7 +772,7 @@ export default {
|
|||
}
|
||||
.questions-about-service {
|
||||
.service-questions {
|
||||
font-weight: $font-weight-bold;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.cart {
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@
|
|||
<input type="hidden" name="paypalSuccessUrl" :value="piaResponseUrl" />
|
||||
<input type="hidden" name="paypalCancelUrl" :value="piaCancelUrl" />
|
||||
|
||||
<input type="hidden" name="sgCCDeclineURL" :value="piaCancelUrl" />
|
||||
<input type="hidden" name="sgCCDeclineURL" :value="piaErrorUrl" />
|
||||
<input type="hidden" name="sgCCTimeoutURL" :value="piaCancelUrl" />
|
||||
|
||||
<input type="hidden" name="sgTransactionType" value="authorization" />
|
||||
|
|
@ -235,6 +235,7 @@ export default {
|
|||
checkoutUrl: externalUrls.SAFELITE_HOP,
|
||||
piaResponseUrl: applicationConfig.PIA_RESPONSE_URL,
|
||||
piaCancelUrl: applicationConfig.PIA_CANCEL_URL,
|
||||
piaErrorUrl: applicationConfig.PIA_ERROR_URL,
|
||||
paymentType: this.getPaymentType(),
|
||||
authToken: "",
|
||||
authSignature: "",
|
||||
|
|
|
|||
|
|
@ -238,6 +238,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -281,6 +284,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -322,6 +328,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -360,6 +369,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -399,6 +411,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -438,6 +453,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -478,6 +496,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -520,6 +541,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -561,6 +585,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -654,6 +681,9 @@ describe("quote.vue", () => {
|
|||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
},
|
||||
applicationUser: {
|
||||
experiments: [],
|
||||
},
|
||||
order: {
|
||||
lineItems: {
|
||||
glassParts: ["item", "item2"],
|
||||
|
|
@ -671,7 +701,7 @@ describe("quote.vue", () => {
|
|||
inactivePromos: [],
|
||||
},
|
||||
},
|
||||
isExternalParameter: true,
|
||||
externalParameterState: { isExternalParameter: true },
|
||||
externalParameterQuote: {
|
||||
isInsurance: true,
|
||||
servicePackage: "glassonly",
|
||||
|
|
@ -720,7 +750,10 @@ function setupMocks({ customMountOptions }) {
|
|||
baseMixin.methods.ResetExternalParamsAndHideModal = jest.fn();
|
||||
baseMixin.methods.isFormValid = jest.fn().mockReturnValue(true);
|
||||
mountOptions.global.mocks["$store"] = store;
|
||||
store.getters.experimentSettings = "test value";
|
||||
|
||||
mountOptions["attachTo"] = document.body;
|
||||
|
||||
const wrapper = shallowMount(quote, mountOptions);
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
return { wrapper };
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
groupName="ServicePackageQuestion"
|
||||
:availableLineItems="availableLineItems"
|
||||
:isInsuranceSelected="isInsuranceSelected"
|
||||
:isRecalibrationOnOrder="isRecalibrationOnOrder"
|
||||
:shouldHideRecalibration="shouldHideRecalibration"
|
||||
@vapsItemsSelected="vapsItemsSelectedAction"
|
||||
@servicePackageDiscountSelected="servicePackageDiscountSelectedAction"
|
||||
:servicePackage="servicePackage"
|
||||
|
|
@ -42,15 +44,17 @@
|
|||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 col-xl-4">
|
||||
<!-- If Cash AND vehicle does not require recal OR if Cash AND State requires showing recal, then show Afterpay banner -->
|
||||
<afterpayModalBanner
|
||||
v-if="!isInsuranceSelected && !isRecalibrationOnOrder"
|
||||
v-if="showAfterpayBanner"
|
||||
cmsWidgetName="AfterpayModalWidget"
|
||||
:lineItems="lineItems" />
|
||||
|
||||
<!-- If vehicle requires recal AND we are hiding recal pricing info, then show recal disclaimer banner. -->
|
||||
<recalDisclaimer
|
||||
class="mb-0"
|
||||
cmsWidgetName="RecalDisclaimerWidget"
|
||||
v-if="isRecalibrationOnOrder"
|
||||
v-if="showRecalDisclaimer"
|
||||
v-on="{ textLinkClicked: openModalAction }"
|
||||
alertClass="alert-info" />
|
||||
|
||||
|
|
@ -99,6 +103,8 @@ import contentGroupModal from "@/fmg-components/content-group-modal/content-grou
|
|||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||
import afterpayModalBanner from "@/layouts/quote/afterpay-modal-banner/afterpay-modal-banner";
|
||||
import recalDisclaimer from "@/layouts/quote/recal-disclaimer/recal-disclaimer.vue";
|
||||
|
||||
// Supporting files
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";
|
||||
|
|
@ -107,6 +113,8 @@ import { storeActions } from "@/constants/store-actions";
|
|||
import { deepClone } from "@/helpers/object-helper";
|
||||
import store from "@/store";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { experimentUniverses, experimentSettings } from "@/constants/experiments";
|
||||
import { getSessionKeyValue, getUserIdValue } from "@/helpers/heritage-integration/cookie-helper";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
|
|
@ -120,7 +128,6 @@ import {
|
|||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||
import promoModalQuestion from "@/fmg-components/promo-modal-question/promo-modal-question";
|
||||
import { experimentSettings } from "@/constants/experiments";
|
||||
import { partTypeStrings } from "@/constants/part-type-strings";
|
||||
import { containsLineItemWithPartType } from "@/helpers/service-package-helper";
|
||||
import { nextTick } from "vue";
|
||||
|
|
@ -286,7 +293,7 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
if (store.getters.externalParameterQuote.isInsurance == true) {
|
||||
vm.isInsuranceSelected = true;
|
||||
vm.servicePackage = store.getters.externalParameterQuote.servicePackage;
|
||||
|
|
@ -324,6 +331,21 @@ export default {
|
|||
this?.availableLineItems
|
||||
);
|
||||
},
|
||||
shouldHideRecalibration() {
|
||||
const recalSettingValue = experimentMixin.methods.getSettingValue(
|
||||
experimentSettings.RECAL_PRICE_REMOVE
|
||||
);
|
||||
return recalSettingValue;
|
||||
},
|
||||
showAfterpayBanner() {
|
||||
return (
|
||||
!this.isInsuranceSelected &&
|
||||
(!this.isRecalibrationOnOrder || !this.shouldHideRecalibration)
|
||||
);
|
||||
},
|
||||
showRecalDisclaimer() {
|
||||
return this.isRecalibrationOnOrder && this.shouldHideRecalibration;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModalAction(modalName) {
|
||||
|
|
|
|||
|
|
@ -475,6 +475,7 @@ describe("service-package-question.vue, matching business rules for package disp
|
|||
mockProps.availableLineItems.push(recalLineItem);
|
||||
mockProps.availableLineItems.push(driverFrontWiperLineItem);
|
||||
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
|
||||
mockProps.isRecalibrationOnOrder = true;
|
||||
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
|
||||
const wrapper = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ export default {
|
|||
availableLineItems: null,
|
||||
activePromos: null,
|
||||
servicePackage: null,
|
||||
isRecalibrationOnOrder: Boolean,
|
||||
shouldHideRecalibration: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -141,12 +143,6 @@ export default {
|
|||
}));
|
||||
return modifiedAnswers;
|
||||
},
|
||||
isRecalibrationOnOrder() {
|
||||
return containsLineItemWithPartType(
|
||||
partTypeStrings.RECALIBRATION,
|
||||
this.nullSafeAvailableLineItems
|
||||
);
|
||||
},
|
||||
isServicePackageDiscountOnOrder() {
|
||||
return containsLineItemWithPartType(
|
||||
partTypeStrings.SERVICE_PACKAGE_DISCOUNT,
|
||||
|
|
@ -242,7 +238,8 @@ export default {
|
|||
},
|
||||
getPackagePrice(packageName, { discountedPrice = false, servicePackageDiscount = false }) {
|
||||
var lineItemsToPrice = [...this.nullSafeAvailableLineItems];
|
||||
if (this.isRecalibrationOnOrder) {
|
||||
|
||||
if (this.isRecalibrationOnOrder && this.shouldHideRecalibration) {
|
||||
lineItemsToPrice = lineItemsToPrice.filter((item) => {
|
||||
return item.partType != partTypeStrings.RECALIBRATION;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
|
||||
export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
|
||||
if (!serviceZipCode) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
const zipCodeData = await baseMixin.methods.getZipCodeData(serviceZipCode, pageNameToLog);
|
||||
|
|
@ -36,12 +36,12 @@ export async function getPricedMobileFeePart(serviceZipCode, pageNameToLog) {
|
|||
false
|
||||
);
|
||||
|
||||
return Promise.resolve(pricingResults[0]);
|
||||
return pricingResults[0];
|
||||
}
|
||||
|
||||
export async function getServiceabilityDetails(serviceZipCode, lineItems, pageNameToLog) {
|
||||
// Get the Mobile Fee Part
|
||||
const serviceabilityDetails = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
return await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_SERVICEABILITY_DETAILS,
|
||||
{
|
||||
serviceZipCode: serviceZipCode,
|
||||
|
|
@ -50,20 +50,26 @@ export async function getServiceabilityDetails(serviceZipCode, lineItems, pageNa
|
|||
pageNameToLog,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve(serviceabilityDetails);
|
||||
export async function getBillToAccountNumber(providerNumber) {
|
||||
// Get the BillTo Account Number
|
||||
return await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_BILL_TO_ACCOUNT_NUMBER,
|
||||
{ providerNumber: providerNumber },
|
||||
"service-location",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
export async function getShopProviderData(serviceZipCode) {
|
||||
const shopProviderData = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
return await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||
storeActions.GET_PROVIDERS,
|
||||
{
|
||||
serviceZipCode: serviceZipCode,
|
||||
},
|
||||
"service-location"
|
||||
);
|
||||
|
||||
return Promise.resolve(shopProviderData);
|
||||
}
|
||||
|
||||
export async function getAvailabilityRating(
|
||||
|
|
@ -92,5 +98,5 @@ export async function getAvailabilityRating(
|
|||
|
||||
const shopStatus = isGoodAvailability ? "high" : "low";
|
||||
|
||||
return Promise.resolve(shopStatus);
|
||||
return shopStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import mobileLocationModalQuestions from "./mobile-location-modal-questions";
|
|||
import { mount, shallowMount } from "@vue/test-utils";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { getBillToAccountNumber } from "../helpers/service-location-helper/service-location-helper";
|
||||
|
||||
const linkWidgetName = "linkWidgetName";
|
||||
const modalWidgetName = "modalWidgetName";
|
||||
|
|
@ -125,6 +126,9 @@ jest.mock(
|
|||
getServiceabilityDetails: jest.fn((mockServiceZipCode) => {
|
||||
return mockGetServiceabilityDetails(mockServiceZipCode);
|
||||
}),
|
||||
getBillToAccountNumber: jest.fn(() => {
|
||||
return "87291";
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ import { deepClone } from "@/helpers/object-helper";
|
|||
import {
|
||||
getPricedMobileFeePart,
|
||||
getServiceabilityDetails,
|
||||
getBillToAccountNumber,
|
||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||
|
||||
// Validation
|
||||
|
|
@ -91,7 +92,12 @@ import { v4 as uuidv4 } from "uuid";
|
|||
|
||||
export default {
|
||||
name: "mobile-location-modal-questions",
|
||||
emits: ["update:modelValue", "updated-mobile-fee-part", "updated-contains-military-base"],
|
||||
emits: [
|
||||
"update:modelValue",
|
||||
"updated-mobile-fee-part",
|
||||
"updated-contains-military-base",
|
||||
"updated-bill-to-account-number",
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
internalModel: deepClone(this.modelValue),
|
||||
|
|
@ -266,11 +272,16 @@ export default {
|
|||
"service-location"
|
||||
);
|
||||
|
||||
const billToAccountNumber = await getBillToAccountNumber(
|
||||
this.internalModel.zipCodeCtu
|
||||
);
|
||||
|
||||
// update content related to service zip code
|
||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||
this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu);
|
||||
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
||||
|
||||
// update the page level model
|
||||
this.$emit("update:modelValue", this.internalModel);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
@updated-mobile-fee-part="setMobileFeePart"
|
||||
@updated-serviceability="setServiceabilityDetails"
|
||||
@updated-contains-military-base="setContainsMilitaryBase"
|
||||
@updated-bill-to-account-number="setBillToAccountNumber"
|
||||
linkWidgetName="ServiceZipLinkWidget"
|
||||
modalWidgetName="ServiceZipModalWidget" />
|
||||
|
||||
|
|
@ -191,6 +192,7 @@ export default {
|
|||
mobileFeePart: null,
|
||||
zipContainsMilitaryBase: false,
|
||||
zipCodeCtu: null,
|
||||
billToAccountNumber: null,
|
||||
shopProviderData: null,
|
||||
navigatingForward: false,
|
||||
};
|
||||
|
|
@ -437,6 +439,9 @@ export default {
|
|||
this.zipContainsMilitaryBase = val;
|
||||
}
|
||||
},
|
||||
setBillToAccountNumber(val) {
|
||||
this.billToAccountNumber = val;
|
||||
},
|
||||
setCtuForMobile(val) {
|
||||
this.zipCodeCtu = val;
|
||||
},
|
||||
|
|
@ -601,6 +606,14 @@ export default {
|
|||
},
|
||||
false
|
||||
);
|
||||
console.log(this.billToAccountNumber);
|
||||
if (this.billToAccountNumber) {
|
||||
this.dispatchStoreAction(
|
||||
this.storeActions.SAVE_BILL_TO_ACCOUNT_NUMBER,
|
||||
this.billToAccountNumber,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
this.updateAndSaveSupportingItems();
|
||||
this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { mount, shallowMount } from "@vue/test-utils";
|
||||
import serviceZipModalQuestion from "./service-zip-modal-question";
|
||||
import { getBillToAccountNumber } from "../helpers/service-location-helper/service-location-helper";
|
||||
|
||||
jest.mock("@/digital-components/textbox-question/textbox-question", () => ({
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||
|
|
@ -51,6 +52,9 @@ jest.mock(
|
|||
getServiceabilityDetails: jest.fn((mockServiceZipCode) => {
|
||||
return mockGetServiceabilityDetails(mockServiceZipCode);
|
||||
}),
|
||||
getBillToAccountNumber: jest.fn((mockServiceZipCodeCtu) => {
|
||||
return "87291";
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -46,11 +46,17 @@ import alert from "@/ux-components/alert/alert";
|
|||
import {
|
||||
getPricedMobileFeePart,
|
||||
getServiceabilityDetails,
|
||||
getBillToAccountNumber,
|
||||
} from "@/layouts/service-location/helpers/service-location-helper/service-location-helper";
|
||||
|
||||
export default {
|
||||
name: "service-zip-modal-question",
|
||||
emits: ["update:modelValue", "updated-mobile-fee-part", "updated-contains-military-base"],
|
||||
emits: [
|
||||
"update:modelValue",
|
||||
"updated-mobile-fee-part",
|
||||
"updated-contains-military-base",
|
||||
"updated-bill-to-account-number",
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
internalModel: this.copyModel(this.modelValue),
|
||||
|
|
@ -165,10 +171,15 @@ export default {
|
|||
"service-location"
|
||||
);
|
||||
|
||||
const billToAccountNumber = await getBillToAccountNumber(
|
||||
this.internalModel.zipCodeCtu
|
||||
);
|
||||
|
||||
// update content related to service zip code
|
||||
this.$emit("updated-mobile-fee-part", mobileFeePart);
|
||||
this.$emit("updated-serviceability", serviceabilityDetails.data);
|
||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
||||
|
||||
// update the page level model
|
||||
this.$emit("update:modelValue", this.internalModel);
|
||||
|
|
|
|||
|
|
@ -169,6 +169,7 @@ function resetMockStoreData() {
|
|||
zipCode: null,
|
||||
emailAddress: null,
|
||||
},
|
||||
externalParameterState: { isExternalParameter: false },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +413,7 @@ describe("service-zip.vue", () => {
|
|||
describe("service-zip.vue", () => {
|
||||
test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => {
|
||||
// Set up the store with isExternalParameter as true
|
||||
store.getters.isExternalParameter = true;
|
||||
store.getters.externalParameterState = { isExternalParameter: true };
|
||||
|
||||
// Set up the component
|
||||
const wrapper = setupMocks({});
|
||||
|
|
@ -436,7 +437,7 @@ describe("service-zip.vue", () => {
|
|||
});
|
||||
test("should not call forwardButtonAction if isExternalParameter is true and form is invalid", async () => {
|
||||
// Set up the store with isExternalParameter as true
|
||||
store.getters.isExternalParameter = true;
|
||||
store.getters.externalParameterState = { isExternalParameter: true };
|
||||
|
||||
// Set up the component
|
||||
const wrapper = setupMocks({});
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ export default {
|
|||
|
||||
next(async (vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
|
||||
if (isValid) {
|
||||
vm.forwardButtonAction();
|
||||
|
|
@ -188,7 +188,7 @@ export default {
|
|||
|
||||
if (!zipCodeData.isValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
return this.$refs.navbar.removeLoader();
|
||||
|
|
@ -197,7 +197,7 @@ export default {
|
|||
|
||||
if (!zipCodeData.isServiceable) {
|
||||
this.displayNonServiceableZipAlert = true;
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
return this.$refs.navbar.removeLoader();
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ jest.mock("@/store", () => ({
|
|||
glassToReplace: [],
|
||||
},
|
||||
},
|
||||
isExternalParameter: false,
|
||||
externalParameterState: { isExternalParameter: false },
|
||||
externalParameterDamage: {
|
||||
damageType: null,
|
||||
isRepair: null,
|
||||
|
|
@ -813,7 +813,7 @@ describe("vehicle-damage.vue", () => {
|
|||
describe("vehicle-damage.vue", () => {
|
||||
test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => {
|
||||
// Set up the store with isExternalParameter as true
|
||||
store.getters.isExternalParameter = true;
|
||||
store.getters.externalParameterState = { isExternalParameter: true };
|
||||
store.getters.externalParameterDamage = {
|
||||
damageType: "windshieldReplace",
|
||||
isRepair: false,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
||||
<div class="container-fluid page-container-grouped-styles vehicle-damage">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
<div class="col-md-6 col-xl-4">
|
||||
<navbar
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
:isForwardActionDisabled="!isContinueEnabled"
|
||||
:isBackButtonHidden="shouldHideBackButton"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction" />
|
||||
|
|
@ -151,7 +151,7 @@ export default {
|
|||
vm.$refs.backGlassOptions.initializeComponent(
|
||||
resultMap.damageOptions.backGlassOptions.availableReplacementOptions
|
||||
);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
await nextTick();
|
||||
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
|
||||
if (isValid) {
|
||||
|
|
@ -180,6 +180,36 @@ export default {
|
|||
this.attachCustomEvents();
|
||||
},
|
||||
methods: {
|
||||
clearChildEntries() {
|
||||
if (!this.isSideDoorDamageLocation) {
|
||||
this.sideDoorOptionsData = {
|
||||
selectedDoorSides: [],
|
||||
selectedDriverSideReplaceOptions: [],
|
||||
selectedPassengerSideReplaceOptions: [],
|
||||
};
|
||||
} else {
|
||||
if (!this.isDriverSideReplace) {
|
||||
this.sideDoorOptionsData.selectedDriverSideReplaceOptions = [];
|
||||
}
|
||||
if (!this.isPassengerSideReplace) {
|
||||
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions = [];
|
||||
}
|
||||
}
|
||||
if (!this.isRearWindowDamageLocation) {
|
||||
this.selectedRearReplaceOptions = "";
|
||||
}
|
||||
if (!this.isWindshieldDamageLocation) {
|
||||
this.selectedWindshieldOptions = {
|
||||
selectedWindshieldDamageType: "",
|
||||
selectedWindshieldChipCount: null,
|
||||
selectedWindshieldReplaceOptions: [],
|
||||
};
|
||||
} else {
|
||||
if (!this.isWindshieldRepair) {
|
||||
this.selectedWindshieldOptions.selectedWindshieldChipCount = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
if (store.getters.vehicle.carId) {
|
||||
return true;
|
||||
|
|
@ -584,6 +614,94 @@ export default {
|
|||
shouldHideBackButton() {
|
||||
return this.$store.getters.requiresVerifiedRedirecting;
|
||||
},
|
||||
isContinueEnabled() {
|
||||
return (
|
||||
this.isAnyDamageSelected &&
|
||||
this.isWindshieldValid &&
|
||||
this.isRearGlassValid &&
|
||||
this.isSideGlassValid
|
||||
);
|
||||
},
|
||||
isAnyDamageSelected() {
|
||||
return this.selectedDamageLocations != null && this.selectedDamageLocations.length != 0;
|
||||
},
|
||||
isSideGlassValid() {
|
||||
if (this.isSideDoorDamageLocation) {
|
||||
if (
|
||||
this.sideDoorOptionsData.selectedDoorSides == null ||
|
||||
this.sideDoorOptionsData.selectedDoorSides == 0
|
||||
) {
|
||||
// SideDoor is selected, but "which side" question is unanswered
|
||||
return false;
|
||||
} else {
|
||||
if (this.isPassengerSideReplace) {
|
||||
if (
|
||||
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions == null ||
|
||||
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.length == 0
|
||||
) {
|
||||
// PassengerSide is selected, but no sub option is
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.isDriverSideReplace) {
|
||||
if (
|
||||
this.sideDoorOptionsData.selectedDriverSideReplaceOptions == null ||
|
||||
this.sideDoorOptionsData.selectedDriverSideReplaceOptions.length == 0
|
||||
) {
|
||||
// DriverSide is selected, but no sub option is
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
isRearGlassValid() {
|
||||
if (this.isRearWindowDamageLocation) {
|
||||
if (!this.selectedRearReplaceOptions) {
|
||||
// RearDoor is selected, but rear option question is unanswered
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
isWindshieldValid() {
|
||||
if (this.isWindshieldDamageLocation) {
|
||||
if (!this.selectedWindshieldOptions.selectedWindshieldDamageType) {
|
||||
// Windshield is selected but no replace/repair option is selected
|
||||
return false;
|
||||
} else {
|
||||
if (this.hasSplitSingleConflict) {
|
||||
return false;
|
||||
}
|
||||
if (this.isWindshieldRepair) {
|
||||
if (this.hasRepairReplaceConflict) {
|
||||
return false;
|
||||
}
|
||||
if (!this.selectedWindshieldOptions.selectedWindshieldChipCount) {
|
||||
// Repair selected but no chips selected
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions ==
|
||||
null ||
|
||||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions
|
||||
.length == 0
|
||||
) {
|
||||
// Replace selected but follow-up split/single question not answered
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedDamageLocations() {
|
||||
this.clearChildEntries();
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export default {
|
|||
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
|
||||
})
|
||||
);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ jest.mock("@/store", () => ({
|
|||
applicationUser: {
|
||||
experiments: [{ universeName: "ConceptFunnel" }],
|
||||
},
|
||||
isExternalParameter: false,
|
||||
externalParameterState: { isExternalParameter: false },
|
||||
externalParameterVehicle: {
|
||||
year: null,
|
||||
make: null,
|
||||
|
|
@ -94,7 +94,7 @@ describe("vehicle.vue", () => {
|
|||
|
||||
describe("vehicle.vue", () => {
|
||||
test("should call forwardButtonAction if isExternalParameter is true and form is valid", async () => {
|
||||
store.getters.isExternalParameter = true;
|
||||
store.getters.externalParameterState.isExternalParameter = true;
|
||||
// Create a shallow mount of MyComponent
|
||||
const { wrapper } = setupMocks();
|
||||
// Set displayNoServiceAlert to false
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ export default {
|
|||
let resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
//ExternalParameter widget have different case style value so need to parse with NextGen
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
if (
|
||||
makeQuestionInitialDataPromise &&
|
||||
modelQuestionInitialDataPromise &&
|
||||
|
|
@ -269,7 +269,7 @@ export default {
|
|||
resultMap.modelQuestionInitialData,
|
||||
resultMap.styleQuestionInitialData
|
||||
);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
await vm.getVehicleDetails();
|
||||
if (!vm.displayNoServiceAlert) {
|
||||
const isValid = await baseMixin.methods.isFormValid(vm.$refs.theForm);
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export default {
|
|||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
baseMixin.methods.ResetExternalParamsAndHideModal();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@ export default {
|
|||
const currentPageName = getPageNameByQueryString();
|
||||
await this.validateSession();
|
||||
|
||||
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
||||
|
||||
const refSequenceNum =
|
||||
store.getters.order.referralSequenceNumber ||
|
||||
store.getters.submittedOrder?.referralSequenceNumber;
|
||||
submittedOrder?.referralSequenceNumber;
|
||||
|
||||
var payload = {
|
||||
userId: getUserIdValue(),
|
||||
|
|
@ -125,8 +127,9 @@ export default {
|
|||
const isDefined = (x) => x !== null && x !== undefined;
|
||||
|
||||
// Get correct order object
|
||||
const hasSubmittedOrder = store.getters.hasSubmittedOrder;
|
||||
const order = hasSubmittedOrder ? store.getters.submittedOrder : store.getters.order;
|
||||
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
|
||||
const submittedOrder = baseMixin.methods.getSubmittedOrder();
|
||||
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
|
||||
|
||||
// Begin assembling payload for data layer
|
||||
const payload = {};
|
||||
|
|
@ -423,7 +426,9 @@ export default {
|
|||
} else {
|
||||
coupon += productSku;
|
||||
}
|
||||
|
||||
discount += productPrice * -1;
|
||||
|
||||
} else {
|
||||
products.push({
|
||||
productType: productType,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import {
|
|||
getSessionKeyValue,
|
||||
getUserIdValue,
|
||||
} from "@/helpers/heritage-integration/cookie-helper";
|
||||
import baseMixin from "./base-mixin";
|
||||
|
||||
const parts = {
|
||||
windshield: {
|
||||
|
|
@ -470,8 +471,9 @@ describe("analyticsMixin.js", () => {
|
|||
test("Pushes correct data when submitted order is present", () => {
|
||||
// Arrange
|
||||
window.dataLayer = [];
|
||||
store.getters.hasSubmittedOrder = true;
|
||||
store.getters.submittedOrder = store.getters.order;
|
||||
baseMixin.methods.hasSubmittedOrder = jest.fn().mockReturnValue(true);
|
||||
baseMixin.methods.getSubmittedOrder = jest.fn().mockReturnValue(store.getters.order);
|
||||
|
||||
store.getters.order = {
|
||||
vehicle: {
|
||||
year: null,
|
||||
|
|
|
|||
|
|
@ -102,6 +102,12 @@ export default {
|
|||
});
|
||||
return filteredLineItems;
|
||||
},
|
||||
filterOutRecalibration(lineItems) {
|
||||
const filteredLineItems = lineItems.filter((item) => {
|
||||
return !item.partType.includes(partTypeStrings.RECALIBRATION);
|
||||
});
|
||||
return filteredLineItems;
|
||||
},
|
||||
getTotalPriceOfAllLineItemsAndChildParts(lineItems, includeTax) {
|
||||
let totalPrice = 0;
|
||||
lineItems.forEach((lineItem) => {
|
||||
|
|
@ -160,9 +166,7 @@ export default {
|
|||
);
|
||||
}
|
||||
|
||||
const order = store.getters.hasSubmittedOrder
|
||||
? store.getters.submittedOrder
|
||||
: store.getters.order;
|
||||
const order = this.hasSubmittedOrder() ? this.getSubmittedOrder() : store.getters.order;
|
||||
|
||||
if (
|
||||
order.payment.insuranceCoverage.isVerified &&
|
||||
|
|
@ -218,6 +222,12 @@ export default {
|
|||
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
|
||||
}
|
||||
},
|
||||
getSubmittedOrder() {
|
||||
return JSON.parse(window.sessionStorage.getItem("submittedOrder"));
|
||||
},
|
||||
hasSubmittedOrder() {
|
||||
return window.sessionStorage.getItem("submittedOrder") !== null;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
storeActions() {
|
||||
|
|
|
|||
|
|
@ -226,15 +226,16 @@ describe("baseMixin.js", () => {
|
|||
const payment = { insuranceCoverage: { isVerified: true } };
|
||||
const policy = { isNoComp: false, isItac: false, currentDeductible: 250 };
|
||||
|
||||
mixIn.methods.hasSubmittedOrder = jest.fn().mockReturnValue(true);
|
||||
mixIn.methods.getSubmittedOrder = jest.fn().mockReturnValue({
|
||||
payment: payment,
|
||||
policy: policy,
|
||||
});
|
||||
|
||||
store.getters = {
|
||||
payment: {},
|
||||
policy: {},
|
||||
order: {},
|
||||
submittedOrder: {
|
||||
payment: payment,
|
||||
policy: policy,
|
||||
},
|
||||
hasSubmittedOrder: true,
|
||||
};
|
||||
|
||||
var amtDue = mixIn.methods.getAmountDue(lineItems);
|
||||
|
|
@ -301,6 +302,8 @@ describe("baseMixin.js", () => {
|
|||
const payment = { isInsurance: null, insuranceCoverage: { isVerified: null } };
|
||||
const policy = { isNoComp: false, isItac: false, currentDeductible: 0 };
|
||||
|
||||
mixIn.methods.hasSubmittedOrder = jest.fn().mockReturnValue(false);
|
||||
|
||||
store.getters = {
|
||||
payment: payment,
|
||||
policy: policy,
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const routes = [
|
|||
}
|
||||
|
||||
// Intercept all navigation if a submitted order exists in storage
|
||||
if (store.getters.hasSubmittedOrder) {
|
||||
if (window.sessionStorage.getItem("submittedOrder") !== null) {
|
||||
if (to.query.fmgPage !== funnelStartPageName) {
|
||||
to.query.fmgPage = fmgPageValues.CONFIRMATION;
|
||||
}
|
||||
|
|
@ -251,7 +251,7 @@ router.beforeEach(async (to, from, next) => {
|
|||
const notToPIAReturn = toQueryPage != fmgPageValues.PAYMENT_PIA_RETURN;
|
||||
const isInIframe = window !== window.top;
|
||||
|
||||
if (store.getters.isExternalParameter) {
|
||||
if (store.getters.externalParameterState?.isExternalParameter) {
|
||||
// Check if alert event is on the bus
|
||||
const alertEvent = eventBus.readEventFromBus(
|
||||
globalEvents.Categories.GLOBAL_ALERT,
|
||||
|
|
@ -301,7 +301,7 @@ router.afterEach(async (to, from) => {
|
|||
await saveSession({ pageNameToLog: to.query.fmgPage });
|
||||
}
|
||||
}
|
||||
if (!store.getters.isExternalParameter) {
|
||||
if (!store.getters.externalParameterState?.isExternalParameter) {
|
||||
showFmgLoadingModal(false);
|
||||
}
|
||||
|
||||
|
|
@ -426,6 +426,7 @@ async function navigate(
|
|||
const zip = getQuerystringParameter(queryStrings.ZIP_CODE);
|
||||
const promo = getQuerystringParameter(queryStrings.PROMO);
|
||||
const pageError = getQuerystringParameter(queryStrings.PAGE_ERROR);
|
||||
const log = getQuerystringParameter(queryStrings.LOG);
|
||||
|
||||
if (
|
||||
hasZip &&
|
||||
|
|
@ -445,6 +446,10 @@ async function navigate(
|
|||
queryStringsObject[queryStrings.PAGE_ERROR] = pageError;
|
||||
}
|
||||
|
||||
if (log) {
|
||||
queryStringsObject[queryStrings.LOG] = log;
|
||||
}
|
||||
|
||||
router.push({
|
||||
name: "root",
|
||||
query: Object.assign(optionalQuery, queryStringsObject),
|
||||
|
|
@ -627,6 +632,7 @@ function updateExternalParameterState() {
|
|||
const externalParameterVinSelection = getQuerystringParameter(queryStrings.VIN_SELECTION);
|
||||
const externalParameterServicePackage = getQuerystringParameter(queryStrings.SERVICE_PACKAGE);
|
||||
const externalParameterNumberOfChips = getQuerystringParameter(queryStrings.NUMBER_OF_CHIPS);
|
||||
const externalParameterSource = getQuerystringParameter(queryStrings.EXPERIMENTS);
|
||||
if (
|
||||
externalParameterYear &&
|
||||
externalParameterMake &&
|
||||
|
|
@ -676,6 +682,12 @@ function updateExternalParameterState() {
|
|||
externalParameterVinSelection
|
||||
);
|
||||
}
|
||||
if (externalParameterSource) {
|
||||
store.commit(
|
||||
storeMutations.UPDATE_EXTERNAL_PARAMETER_SOURCE,
|
||||
externalParameterSource
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default router;
|
||||
|
|
|
|||
|
|
@ -472,6 +472,10 @@ export const mutations = {
|
|||
externalParameterState.quote.servicePackage = servicePackage;
|
||||
saveExternalParameterState(externalParameterState);
|
||||
},
|
||||
updateExternalParameterSource(state, source) {
|
||||
externalParameterState.source = source;
|
||||
saveExternalParameterState(externalParameterState);
|
||||
},
|
||||
//RESET ExternalParameter MUTATIONS
|
||||
resetExternalParameterVehicleState(state) {
|
||||
externalParameterState.vehicle.year = null;
|
||||
|
|
@ -502,6 +506,10 @@ export const mutations = {
|
|||
externalParameterState.quote.servicePackage = null;
|
||||
saveExternalParameterState(externalParameterState);
|
||||
},
|
||||
resetExternalParameterSourceState(state) {
|
||||
externalParameterState.source = null;
|
||||
saveExternalParameterState(externalParameterState);
|
||||
},
|
||||
resetIsExternalParameter(state) {
|
||||
externalParameterState.isExternalParameter = null;
|
||||
saveExternalParameterState(externalParameterState);
|
||||
|
|
@ -883,8 +891,6 @@ export const getters = {
|
|||
.filter((x) => !!x.isActive)
|
||||
.map((x) => x.settings)
|
||||
.reduce((r, c) => Object.assign(r, c), {}) ?? {},
|
||||
submittedOrder: (state) => JSON.parse(window.sessionStorage.getItem("submittedOrder")),
|
||||
hasSubmittedOrder: (state) => window.sessionStorage.getItem("submittedOrder") !== null,
|
||||
isVerifiedAndDeductibleZeroConfirmed: (state) => {
|
||||
// used as a CMS variable on /payment-method, needed for FunnelSubHeaderWidget on cart pages
|
||||
if (
|
||||
|
|
@ -900,7 +906,7 @@ export const getters = {
|
|||
requiresVerifiedRedirecting: (state) => {
|
||||
return state.order?.referralNumber?.length === 6;
|
||||
},
|
||||
isExternalParameter: (state) => externalParameterState?.isExternalParameter,
|
||||
externalParameterState: (state) => externalParameterState,
|
||||
externalParameterVehicle: (state) => externalParameterState?.vehicle,
|
||||
externalParameterDamage: (state) => externalParameterState?.vehicleDamage,
|
||||
externalParameterQuote: (state) => externalParameterState?.quote,
|
||||
|
|
@ -1883,6 +1889,7 @@ export const actions = {
|
|||
},
|
||||
isInsurance: order.payment.isInsurance,
|
||||
parentAccountNumber: order.payment.parentAccountNumber,
|
||||
billToAccountNumber: order.payment.billToAccountNumber,
|
||||
inactivePromos: order.payment.inactivePromos,
|
||||
isCreditCard:
|
||||
order.payment.piaType == paymentMethods.CREDIT_CARD ? true : false,
|
||||
|
|
@ -2415,14 +2422,6 @@ export const actions = {
|
|||
context,
|
||||
{ payload: { availableLineItems, serviceZipCode, serviceZipCodeCtu }, pageNameToLog }
|
||||
) {
|
||||
const zipCodeToUse = serviceZipCode
|
||||
? serviceZipCode
|
||||
: context.getters.order.serviceLocation.zipCode;
|
||||
|
||||
const ctuToUse = serviceZipCodeCtu
|
||||
? serviceZipCodeCtu
|
||||
: context.getters.order.serviceLocation.zipCodeCtu;
|
||||
|
||||
const flattenedLineItemsWithChildParts =
|
||||
getFlattenedArrayOfLineItemsWithChildParts(availableLineItems);
|
||||
|
||||
|
|
@ -2430,34 +2429,40 @@ export const actions = {
|
|||
partNumber: lineItem.partNumber,
|
||||
}));
|
||||
|
||||
const availableLineItemsFormattedForRequest =
|
||||
buildQueryStringParameterFromArrayOfComplexObjects(
|
||||
lineItemsWithOnlyPartNumbers,
|
||||
"lineItems"
|
||||
);
|
||||
const order = context.getters.order;
|
||||
|
||||
const vehicle = context.getters.order.vehicle;
|
||||
|
||||
// TODO: Insurance pricing...`ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}`
|
||||
let queryString =
|
||||
`ParentAccountNumber=${context.getters.order.payment.parentAccountNumber}` +
|
||||
`&CTU=${ctuToUse}` +
|
||||
`&CarId=${vehicle.carId}` +
|
||||
`&Make=${vehicle.make}` +
|
||||
`&Model=${vehicle.model}` +
|
||||
`&Year=${vehicle.year}` +
|
||||
`&EON=${context.getters.order.eon}` +
|
||||
`&ZipCode=${zipCodeToUse}` +
|
||||
`&${availableLineItemsFormattedForRequest}`;
|
||||
|
||||
const lineItemServerData = context.getters.order.lineItems.serverData;
|
||||
if (lineItemServerData) {
|
||||
queryString += `&ServerData=${encodeURIComponent(lineItemServerData)}`;
|
||||
var providerNumber =
|
||||
order.serviceLocation?.provider?.providerNumber ?? order.serviceLocation?.zipCodeCtu;
|
||||
if (providerNumber.startsWith("00") && providerNumber.length > 5) {
|
||||
providerNumber = providerNumber.substring(1);
|
||||
}
|
||||
|
||||
const response = await globalMethods.callHttpClient({
|
||||
method: endpoints.PriceOrderItems.method,
|
||||
endpoint: `${endpoints.PriceOrderItems.url}?${queryString}`,
|
||||
endpoint: endpoints.PriceOrderItems.url,
|
||||
payload: {
|
||||
parentAccountNumber: order.payment?.parentAccountNumber,
|
||||
billToAccountNumber:
|
||||
order.payment?.billToAccountNumber ??
|
||||
applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER,
|
||||
providerNumber: providerNumber,
|
||||
carId: order.vehicle.carId,
|
||||
year: order.vehicle.year,
|
||||
make: order.vehicle.make,
|
||||
model: order.vehicle.model,
|
||||
eon: order.eon,
|
||||
state: order.serviceLocation?.state,
|
||||
referralNumber: order.referralNumber,
|
||||
referralSequenceNumber: order.referralSequenceNumber,
|
||||
zipCode: order.serviceLocation?.zipCode,
|
||||
serviceZipCode: serviceZipCode ?? order.serviceLocation?.zipCode,
|
||||
referralDate: order.referralDate,
|
||||
isReplacement: !order.damage.isRepair,
|
||||
deductible: order.policy?.currentDeductible,
|
||||
coverageStatus: order.payment?.insuranceCoverage?.coverageStatus,
|
||||
lineItems: lineItemsWithOnlyPartNumbers,
|
||||
serverData: order.lineItems.serverData ? order.lineItems.serverData : "",
|
||||
},
|
||||
logApiCall: true,
|
||||
pageNameToLog: pageNameToLog,
|
||||
});
|
||||
|
|
@ -2674,6 +2679,10 @@ export const actions = {
|
|||
|
||||
return revalidateResponse?.data;
|
||||
},
|
||||
///////////////////////////
|
||||
// Account Service Calls //
|
||||
///////////////////////////
|
||||
|
||||
// List all insurance companies
|
||||
async getInsuranceCompanyList(context, { pageNameToLog }) {
|
||||
const insuranceCompanyList = await globalMethods.callHttpClient({
|
||||
|
|
@ -2696,6 +2705,40 @@ export const actions = {
|
|||
return 0;
|
||||
});
|
||||
},
|
||||
// Get BillToAccountNumber
|
||||
async getBillToAccountNumber(
|
||||
context,
|
||||
{
|
||||
payload: {
|
||||
parentAccountNumber = context.getters.order.payment.parentAccountNumber,
|
||||
providerNumber = context.getters.order.serviceLocation.zipCodeCtu,
|
||||
isItac = context.getters.order.policy.isItac,
|
||||
},
|
||||
pageNameToLog,
|
||||
}
|
||||
) {
|
||||
// Cash orders default to 87291 - update if/when more detailed
|
||||
// billToAccountNumbers are needed for cash
|
||||
if (parentAccountNumber == applicationConfig.CASH_PARENT_ACCOUNT_NUMBER) {
|
||||
return applicationConfig.CASH_DEFAULT_BILL_TO_ACCOUNT_NUMBER;
|
||||
}
|
||||
const queryString =
|
||||
`ParentAccountNumber=${parentAccountNumber}` +
|
||||
`&ProviderNumber=${providerNumber}` +
|
||||
`&IsItac=${isItac}`;
|
||||
|
||||
const billToAccountNumberResponse = await globalMethods.callHttpClient({
|
||||
method: endpoints.GetBillToAccountNumber.method,
|
||||
endpoint: `${endpoints.GetBillToAccountNumber.url}?${queryString}`,
|
||||
logApiCall: true,
|
||||
pageNameToLog: pageNameToLog,
|
||||
});
|
||||
|
||||
return billToAccountNumberResponse.data.toString();
|
||||
},
|
||||
//////////////////////////////////
|
||||
// END OF Account Service Calls //
|
||||
//////////////////////////////////
|
||||
|
||||
// Misc order actions
|
||||
saveSchedule(context, scheduleInfo) {
|
||||
|
|
@ -2798,7 +2841,7 @@ export const actions = {
|
|||
},
|
||||
|
||||
createSubmittedOrder(context) {
|
||||
if (context.getters.hasSubmittedOrder) {
|
||||
if (window.sessionStorage.getItem("submittedOrder") !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3368,6 +3411,7 @@ function createExternalParameterDefaultState() {
|
|||
isInsurance: null,
|
||||
servicePackage: null,
|
||||
},
|
||||
source: null,
|
||||
};
|
||||
// set to session storage
|
||||
saveExternalParameterState(externalParameterDefaultState);
|
||||
|
|
|
|||
Loading…
Reference in a new issue