DigitalConsumer.FixMyGlass/src/store/index.js

2817 lines
110 KiB
JavaScript

import { createStore } from "vuex";
import { endpoints } from "@/constants/endpoints.js";
import { storeMutations } from "@/constants/store-mutations";
import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
import createPersistedState from "vuex-persistedstate";
import globalMethods from "@/global-methods";
import { storeActions } from "@/constants/store-actions";
import { applicationConfig } from "@/constants/application-config";
import { experimentTriggers } from "@/constants/experiments";
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
import { singleWindshieldCarIds } from "@/constants/single-windshield-carids";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper.js";
import { deepEqual } from "@/helpers/object-helper";
import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { deepClone } from "@/helpers/object-helper";
import { queryStrings } from "@/constants/query-strings";
import { partTypeStrings } from "@/constants/part-type-strings";
import {
convertDateStringToDate,
militaryToTwelveHourTime,
getDisplayTextForDurationLength,
} from "@/layouts/schedule/helpers/schedule-helper";
import { paymentMethods } from "@/constants/payment-method-constants";
import { getDateDifferenceInDays } from "@/helpers/date-helper";
// Export State
const getDefaultState = () => {
return {
order: {
vehicle: {
year: null,
make: null,
model: null,
style: null,
carId: null,
category: null,
vin: null,
imageUrl: null,
imageVifNumber: null,
imageColor: null,
registration: {
licensePlate: null,
},
},
serviceLocation: {
address: null,
address2: null,
city: null,
state: null,
zipCode: null,
zipCodeCtu: null,
appointmentType: null,
isVehicleProtected: null,
provider: {
providerNumber: null,
address: {
streetAddress: null,
city: null,
state: null,
zipCode: null,
zipCodeCtu: null,
},
},
techNotes: null,
},
customer: {
firstName: null,
lastName: null,
emailAddress: null,
phoneNumber: null,
isSmsOptIn: null,
},
damage: {
isRepair: null,
numberOfChips: null,
glassToReplace: null,
partQuestionAnswers: null,
moldingQuestionAnswers: null,
capabilityQuestionAnswers: null,
},
lineItems: {
glassParts: null,
supportingItems: null,
vaps: null,
serverData: null,
promos: null,
},
payment: {
isInsurance: null,
insuranceCoverage: {
isVerified: null,
coverageStatus: null,
},
parentAccountNumber: 0,
isPia: null,
piaType: null,
inactivePromos: null,
paypalToken: null,
ccToken: {
subscriptionId: null,
expMonth: null,
expYear: null,
cardType: null,
billToPostalCode: null,
billToFirstName: null,
billToLastName: null,
referenceNumber: null,
authCode: null,
transactionId: null,
transReferenceNumber: null,
},
},
schedule: {
date: null,
startTime: null,
endTime: null,
routeCode: null,
jobMaxMinutes: null,
jobMinMinutes: null,
},
referralNumber: null,
referralSequenceNumber: null,
referralDate: null,
referralCorrelationId: null,
eon: null,
workOrderNumber: null,
customerPortalLoginToken: null,
},
applicationUser: {
eventBus: [],
pageData: {},
savedSessionTimeout: getDateForSavedSessionTimeout(),
saveSessionPromise: null,
savedSessionId: null,
crmCustomerId: null,
lastPageVisited: null,
experiments: [],
triggeredSiteEntry: false,
},
};
};
export const state = getDefaultState();
// Export Mutations
export const mutations = {
// VEHICLE MUTATIONS
updateYear(state, year) {
state.order.vehicle.year = year;
},
updateMake(state, make) {
state.order.vehicle.make = make;
},
updateModel(state, model) {
state.order.vehicle.model = model;
},
updateStyle(state, style) {
state.order.vehicle.style = style;
},
updateCarId(state, carId) {
state.order.vehicle.carId = carId;
},
updateVehicleCategory(state, category) {
state.order.vehicle.category = category;
},
updateVehicleImageUrl(state, imageUrl) {
state.order.vehicle.imageUrl = imageUrl;
},
updateVehicleImageVifNumber(state, imageVifNumber) {
state.order.vehicle.imageVifNumber = imageVifNumber;
},
updateVehicleImageColor(state, imageColor) {
state.order.vehicle.imageColor = imageColor;
},
updateVehicleVin(state, vin) {
state.order.vehicle.vin = vin;
},
updateIsRepair(state, isRepair) {
state.order.damage.isRepair = isRepair;
},
updateNumberOfChips(state, numberOfChips) {
state.order.damage.numberOfChips = numberOfChips;
},
updateGlassToReplace(state, glassToReplace) {
state.order.damage.glassToReplace = glassToReplace;
},
updatePartQuestionAnswers(state, answersArray) {
state.order.damage.partQuestionAnswers = answersArray;
},
updateMoldingQuestionAnswers(state, answersArray) {
state.order.damage.moldingQuestionAnswers = answersArray;
},
updateCapabilityQuestionAnswers(state, answersArray) {
state.order.damage.capabilityQuestionAnswers = answersArray;
},
updateGlassParts(state, partsData) {
state.order.lineItems.glassParts = partsData;
},
updateVaps(state, partsData) {
state.order.lineItems.vaps = partsData;
},
updateSupportingItems(state, partsData) {
state.order.lineItems.supportingItems = partsData;
},
updateLineItemsServerData(state, serverData) {
state.order.lineItems.serverData = serverData;
},
updateInactivePromos(state, inactivePromos) {
state.order.payment.inactivePromos = inactivePromos;
},
updatePromos(state, promos) {
state.order.lineItems.promos = promos;
},
updatePageData(state, pageData) {
state.applicationUser.pageData[pageData.page] = pageData.data;
},
updateReferralCorrelationId(state, referralCorrelationId) {
state.order.referralCorrelationId = referralCorrelationId;
},
updateReferralNumber(state, referralNumber) {
state.order.referralNumber = referralNumber;
},
updateReferralSequenceNumber(state, referralSequenceNumber) {
state.order.referralSequenceNumber = referralSequenceNumber;
},
updateReferralDate(state, referralDate) {
state.order.referralDate = referralDate;
},
updateParentAcctNumber(state, parentAcctNumber) {
state.order.payment.parentAccountNumber = parentAcctNumber;
},
updateEON(state, eon) {
state.order.eon = eon;
},
updateIsInsurance(state, isInsurance) {
state.order.payment.isInsurance = isInsurance;
},
updateIsPia(state, isPia) {
state.order.payment.isPia = isPia;
},
updatePiaType(state, piaType) {
state.order.payment.piaType = piaType;
},
updateWorkOrderNumber(state, workOrderNumber) {
state.order.workOrderNumber = workOrderNumber;
},
updateCustomerPortalLoginToken(state, customerPortalLoginToken) {
state.order.customerPortalLoginToken = customerPortalLoginToken;
},
updateCCToken(state, ccToken) {
state.order.payment.ccToken.subscriptionId = ccToken.subscriptionId;
state.order.payment.ccToken.expMonth = ccToken.expMonth;
state.order.payment.ccToken.expYear = ccToken.expYear;
state.order.payment.ccToken.cardType = ccToken.cardType;
state.order.payment.ccToken.billToPostalCode = ccToken.billToPostalCode;
state.order.payment.ccToken.billToFirstName = ccToken.billToFirstName;
state.order.payment.ccToken.billToLastName = ccToken.billToLastName;
state.order.payment.ccToken.referenceNumber = ccToken.referenceNumber;
state.order.payment.ccToken.authCode = ccToken.authCode;
state.order.payment.ccToken.transactionId = ccToken.transactionId;
state.order.payment.ccToken.transReferenceNumber = ccToken.transReferenceNumber;
},
updatePaypalToken(state, ppToken) {
state.order.payment.paypalToken = ppToken;
},
updateInsuranceVerifiedStatus(state, isVerified) {
state.order.payment.insuranceCoverage.isVerified = isVerified;
},
updateCustomerEmailAddress(state, customerEmailAddress) {
state.order.customer.emailAddress = customerEmailAddress;
},
updateVehicle(state, vehicleInfo) {
state.order.vehicle.year = vehicleInfo.year;
state.order.vehicle.make = vehicleInfo.make;
state.order.vehicle.model = vehicleInfo.model;
state.order.vehicle.style = vehicleInfo.style;
state.order.vehicle.carId = vehicleInfo.carId;
state.order.vehicle.category = vehicleInfo.category;
state.order.vehicle.vin = vehicleInfo.vin;
state.order.vehicle.imageUrl = vehicleInfo.imageUrl;
state.order.vehicle.imageVifNumber = vehicleInfo.imageVifNumber;
state.order.vehicle.imageColor = vehicleInfo.imageVifColor;
},
updateRegistration(state, registrationInfo) {
state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate;
},
updateServiceZip(state, serviceZipInfo) {
state.order.serviceLocation.state = serviceZipInfo.state;
state.order.serviceLocation.zipCode = serviceZipInfo.zipCode;
state.order.serviceLocation.zipCodeCtu = serviceZipInfo.zipCodeCtu;
},
updateServiceLocation(state, serviceLocationInfo) {
state.order.serviceLocation.address = serviceLocationInfo.address;
state.order.serviceLocation.address2 = serviceLocationInfo.address2;
state.order.serviceLocation.city = serviceLocationInfo.city;
state.order.serviceLocation.state = serviceLocationInfo.state;
state.order.serviceLocation.zipCode = serviceLocationInfo.zipCode;
state.order.serviceLocation.zipCodeCtu = serviceLocationInfo.zipCodeCtu;
state.order.serviceLocation.appointmentType = serviceLocationInfo.appointmentType;
state.order.serviceLocation.isVehicleProtected = serviceLocationInfo.isVehicleProtected;
state.order.serviceLocation.provider = {
providerNumber: serviceLocationInfo.provider?.providerNumber,
address: {
streetAddress: serviceLocationInfo.provider?.address?.streetAddress,
city: serviceLocationInfo.provider?.address?.city,
state: serviceLocationInfo.provider?.address?.state,
zipCode: serviceLocationInfo.provider?.address?.zipCode,
zipCodeCtu: serviceLocationInfo.provider?.address?.zipCodeCtu,
},
};
},
updateServiceLocationTechNotes(state, techNotes) {
state.order.serviceLocation.techNotes = techNotes;
},
updateSchedule(state, scheduleInfo) {
if (scheduleInfo) {
state.order.schedule.date = scheduleInfo.date;
state.order.schedule.startTime = scheduleInfo.startTime;
state.order.schedule.endTime = scheduleInfo.endTime;
state.order.schedule.routeCode = scheduleInfo.routeCode;
state.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes;
state.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes;
}
},
updateCustomerDetails(state, customerDetails) {
if (customerDetails) {
state.order.customer.firstName = customerDetails.firstName;
state.order.customer.lastName = customerDetails.lastName;
state.order.customer.emailAddress = customerDetails.emailAddress;
state.order.customer.phoneNumber = customerDetails.phoneNumber;
state.order.customer.isSmsOptIn = customerDetails.isSmsOptIn;
}
},
// applicationUser MUTATIONS
updateSaveSessionPromise(state, saveSessionPromise) {
state.applicationUser.saveSessionPromise = saveSessionPromise;
},
updateSavedSessionId(state, savedSessionId) {
state.applicationUser.savedSessionId = savedSessionId;
},
updateCrmCustomerId(state, crmCustomerId) {
state.applicationUser.crmCustomerId = crmCustomerId;
},
updateLastPageVisited(state, lastPageVisited) {
state.applicationUser.lastPageVisited = lastPageVisited;
},
// EVENT BUS MUTATIONS
addEventToBus(state, event) {
state.applicationUser.eventBus.push(event);
},
removeEventFromBus(state, eventData) {
const matchedEvent = state.applicationUser.eventBus.find(
({ category, subCategory }) =>
category === eventData.category && subCategory === eventData.subCategory
);
const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent);
// If the item exists, remove it.
if (itemIndex > -1) {
state.applicationUser.eventBus.splice(itemIndex, 1);
}
},
// RESET DEPENDENCY MUTATIONS
resetVehicleState(state) {
state.order.vehicle.year = null;
state.order.vehicle.make = null;
state.order.vehicle.model = null;
state.order.vehicle.style = null;
state.order.vehicle.carId = null;
state.order.vehicle.category = null;
state.order.vehicle.vin = null;
state.order.vehicle.imageUrl = null;
state.order.vehicle.imageVifNumber = null;
state.order.vehicle.imageColor = null;
},
resetDamageState(state) {
state.order.damage.isRepair = null;
state.order.damage.numberOfChips = null;
state.order.damage.glassToReplace = null;
},
resetRegistrationState(state) {
state.order.vehicle.registration.licensePlate = null;
},
resetGlassPartsState(state) {
state.order.lineItems.glassParts = null;
state.order.damage.partQuestionAnswers = null;
state.order.damage.moldingQuestionAnswers = null;
state.order.damage.capabilityQuestionAnswers = null;
state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null;
},
resetSchedule(state) {
state.order.schedule.date = null;
state.order.schedule.startTime = null;
state.order.schedule.endTime = null;
state.order.schedule.routeCode = null;
state.order.schedule.jobMaxMinutes = null;
state.order.schedule.jobMinMinutes = null;
//premium appointment fee used on schedule page also needs reset when schedule is reset
const supportingItems = state.order.lineItems.supportingItems;
const premiumAppointmentFeeIndex = supportingItems?.findIndex(
(item) => item.partType == PREMIUM_FEE_PART_TYPE
);
if (premiumAppointmentFeeIndex >= 0) {
supportingItems.splice(premiumAppointmentFeeIndex, 1);
state.order.lineItems.supportingItems = supportingItems;
}
},
resetState(state) {
Object.assign(state, getDefaultState());
},
resetSaveSessionPromise(state) {
state.applicationUser.saveSessionPromise = null;
},
resetServiceLocationAppointmentType(state) {
state.order.serviceLocation.appointmentType = null;
},
resetServiceLocationProvider(state) {
state.order.serviceLocation.provider = {
providerNumber: null,
address: {
streetAddress: null,
city: null,
state: null,
zipCode: null,
zipCodeCtu: null,
},
};
},
resetServiceLocationMobileAddress(state) {
state.order.serviceLocation.address = null;
state.order.serviceLocation.address2 = null;
state.order.serviceLocation.city = null;
state.order.serviceLocation.state = null;
state.order.serviceLocation.isVehicleProtected = null;
},
resetPaymentMethodChoice(state) {
state.order.payment.isPia = null;
state.order.payment.piaType = null;
},
// Misc Mutations
updateStateWithOrderInformation(state, sessionInformation) {
state.order.referralNumber = sessionInformation.order.referralNumber;
state.order.referralSequenceNumber = sessionInformation.order.referralSequenceNumber;
state.order.referralDate = sessionInformation.order.referralDate;
state.order.referralCorrelationId = sessionInformation.order.referralCorrelationId;
state.order.eon = sessionInformation.order.eon;
state.order.customerPortalLoginToken = sessionInformation.order.CustomerPortalLoginToken;
if (state.order.vehicle.vin !== sessionInformation.order.vehicle?.vin) {
state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null;
}
state.order.vehicle = Object.assign(state.order.vehicle, {
year: sessionInformation.order.vehicle?.year,
make: sessionInformation.order.vehicle?.make,
model: sessionInformation.order.vehicle?.model,
style: sessionInformation.order.vehicle?.style,
vin: sessionInformation.order.vehicle?.vin,
carId: sessionInformation.order.vehicle?.carId,
category: sessionInformation.order.vehicle?.category,
imageUrl: sessionInformation.order.vehicle?.imageUrl,
imageVifNumber: sessionInformation.order.vehicle?.imageVifNumber,
imageColor: sessionInformation.order.vehicle?.imageVifColor,
});
state.order.damage.glassToReplace = sessionInformation.order.damage.glassToReplace;
state.order.damage.isRepair = sessionInformation.order.damage.isRepair;
state.order.damage.numberOfChips = sessionInformation.order.damage.numberOfChips;
state.order.damage.partQuestionAnswers =
sessionInformation.order.damage.partQuestionAnswers;
state.order.damage.moldingQuestionAnswers =
sessionInformation.order.damage.moldingQuestionAnswers;
state.order.damage.capabilityQuestionAnswers =
sessionInformation.order.damage.capabilityQuestionAnswers;
state.order.lineItems.glassParts = sessionInformation.order.lineItems.glassParts;
state.order.lineItems.supportingItems = sessionInformation.order.lineItems.supportingItems;
state.order.lineItems.vaps = sessionInformation.order.lineItems.vaps;
state.order.lineItems.promos = sessionInformation.order.lineItems.promos;
state.order.lineItems.serverData = sessionInformation.order.lineItems.serverData;
state.order.payment.parentAccountNumber =
sessionInformation.order.payment.parentAccountNumber;
state.order.payment.inactivePromos = sessionInformation.order.payment.inactivePromos;
state.order.serviceLocation.address =
sessionInformation.order.serviceLocation.streetAddress;
state.order.serviceLocation.address2 =
sessionInformation.order.serviceLocation.streetAddress2;
state.order.serviceLocation.city = sessionInformation.order.serviceLocation.city;
state.order.serviceLocation.state = sessionInformation.order.serviceLocation.state;
state.order.serviceLocation.zipCode = sessionInformation.order.serviceLocation.zipCode;
state.order.serviceLocation.zipCodeCtu =
sessionInformation.order.serviceLocation.zipCodeCtu;
state.order.serviceLocation.appointmentType =
sessionInformation.order.serviceLocation.appointmentType;
state.order.serviceLocation.isVehicleProtected =
sessionInformation.order.serviceLocation.isVehicleProtected;
state.order.serviceLocation.provider.providerNumber =
sessionInformation.order.serviceLocation.provider?.providerNumber;
state.order.serviceLocation.provider.address.streetAddress =
sessionInformation.order.serviceLocation.provider?.address?.streetAddress;
state.order.serviceLocation.provider.address.city =
sessionInformation.order.serviceLocation.provider?.address?.city;
state.order.serviceLocation.provider.address.state =
sessionInformation.order.serviceLocation.provider?.address?.state;
state.order.serviceLocation.provider.address.zipCode =
sessionInformation.order.serviceLocation.provider?.address?.zipCode;
state.order.serviceLocation.provider.address.zipCodeCtu =
sessionInformation.order.serviceLocation.provider?.address?.zipCodeCtu;
state.order.serviceLocation.techNotes = sessionInformation.order.serviceLocation.techNotes;
state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
state.order.payment.insuranceCoverage.isVerified =
sessionInformation?.order.payment.insuranceCoverage.isVerified;
state.order.payment.insuranceCoverage.coverageStatus =
sessionInformation?.insuranceInfo?.coverageStatus;
state.order.customer.emailAddress = sessionInformation.order.customer.emailAddress;
state.order.customer.firstName = sessionInformation.order.customer.firstName;
state.order.customer.lastName = sessionInformation.order.customer.lastName;
state.order.customer.phoneNumber = sessionInformation.order.customer.phoneNumber;
state.order.customer.isSmsOptIn = sessionInformation.order.customer.isSmsOptIn;
state.order.existingPromoCode = sessionInformation.order.existingPromoCode;
state.applicationUser.experiments = sessionInformation.applicationUser.experiments;
state.applicationUser.crmCustomerId = sessionInformation.applicationUser.crmCustomerId;
state.applicationUser.pageData = sessionInformation.applicationUser.pageData;
state.applicationUser.lastPage = sessionInformation.applicationUser.lastPage;
state.order.schedule.date = sessionInformation.order.schedule?.date;
state.order.schedule.startTime = sessionInformation.order.schedule?.startTime;
state.order.schedule.endTime = sessionInformation.order.schedule?.endTime;
state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode;
state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes;
state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes;
},
updateExperiments(state, experiments) {
state.applicationUser.experiments = experiments;
},
updateTriggeredSiteEntry(state, wasSiteEntryTriggered) {
state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
},
};
// Export Getters
export const getters = {
vehicle: (state) => state.order.vehicle,
eventBusItem: (state) => (eventCategory, eventSubCategory) => {
const matchedEvent = state.applicationUser.eventBus.find(
({ category, subCategory }) =>
category === eventCategory && subCategory === eventSubCategory
);
return matchedEvent !== undefined ? matchedEvent.eventValue : undefined;
},
eventBus: (state) => state.applicationUser.eventBus,
damage: (state) => state.order.damage,
hasExactlyOneChip: (state) => {
return state.order.damage?.numberOfChips === 1;
},
hasAnyNonWindshieldGlassParts: (state) => {
const nonWindshieldItems = state.order.damage.glassToReplace?.filter(
(glassToReplace) => glassToReplace.glassLocation != "Windshield"
);
return !!nonWindshieldItems?.length;
},
isMobileAppointment: (state) => {
return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
},
isDropOffAppointment: (state) => {
return state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF;
},
isRecalibrationOnOrder: (state) => {
return getHasRecalibrationPart(state);
},
areRearWipersOnOrder: (state) => {
return !!state.order.lineItems.vaps?.some(
(vap) => vap.partType.toUpperCase() === partTypeStrings.REAR_WIPER.toUpperCase()
);
},
scheduleDisplayText: (state) => {
const dateModel = convertDateStringToDate(state.order.schedule.date);
const readableDate = dateModel?.toLocaleDateString("en-us", {
weekday: "long",
month: "long",
day: "numeric",
year: "numeric",
});
const readableStartTime = militaryToTwelveHourTime(state.order.schedule.startTime);
const readableEndTime = militaryToTwelveHourTime(state.order.schedule.endTime);
const readableDuration = getDisplayTextForDurationLength(
state.order.schedule.jobMinMinutes,
state.order.schedule.jobMaxMinutes
);
return {
date: readableDate,
startTime: readableStartTime,
endTime: readableEndTime,
duration: readableDuration,
};
},
lineItems: (state) => state.order.lineItems,
pageData: (state) => (page) => {
return state.applicationUser.pageData[page];
},
applicationUser: (state) => state.applicationUser,
order: (state) => state.order,
payment: (state) => state.order.payment,
experimentOrder: (state) => {
return {
funnelVehicleYear: state.order.vehicle.year,
funnelVehicleMake: state.order.vehicle.make,
funnelVehicleModel: state.order.vehicle.model,
funnelVehicleStyle: state.order.vehicle.style,
funnelIsRepair: state.order.damage.isRepair,
funnelNumberOfChips: state.order.damage.numberOfChips,
funnelCarId: state.order.vehicle.carId,
funnelServiceCity: state.order.serviceLocation.city,
funnelServiceState: state.order.serviceLocation.state,
funnelServiceZipCode: state.order.serviceLocation.zipCode,
funnelServiceZipCodeCtu: state.order.serviceLocation.zipCodeCtu,
funnelProviderNumber: state.order.serviceLocation.provider.providerNumber,
funnelParentAccountNumber: state.order.payment.parentAccountNumber,
funnelReferralType: state.order.payment.isInsurance ? "INSURANCE" : "CASH QUOTE",
funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
funnelHasRecalibrationPart: getHasRecalibrationPart(state),
funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.WINDSHIELD),
funnelSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.REAR),
funnelSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.DRIVER),
funnelSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.PASSENGER),
funnelOrderPartNumbers: [
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"partNumber"
),
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.supportingItems,
"partNumber"
),
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.vaps,
"partNumber"
),
],
funnelOrderPartTypes: [
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
),
],
};
},
experimentSettings: (state) =>
state.applicationUser.experiments
.filter((x) => !!x.isActive)
.map((x) => x.settings)
.reduce((r, c) => Object.assign(r, c), {}) ?? {},
};
function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
return (array ?? []).map((x) => x[propertyName]).filter((x) => x);
}
function getTimeSlotsAdditionalEventData(
provisionalTriggers,
zipCode,
firstAvailableAppointmentDateString,
shopAppointmentType
) {
var numberOfDays = null;
if (firstAvailableAppointmentDateString)
numberOfDays = getDateDifferenceInDays(
new Date().toISOString().split("T")[0],
firstAvailableAppointmentDateString
);
if (shopAppointmentType)
return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ShopAppointmentType:${shopAppointmentType},ProvisionalTriggers:${provisionalTriggers.join(
","
)}`;
else
return `FirstAvailableAppointment:${numberOfDays},Zip:${zipCode},ProvisionalTriggers:${provisionalTriggers.join(
","
)}`;
}
// Export Actions
export const actions = {
// Vehicle API Actions
getVehicleYears(context, { pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleYears.method,
endpoint: endpoints.GetVehicleYears.url,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
lookupVehicleByYmms(context, { payload: { year, make, model, style }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.LookupVehicleByYmms.method,
endpoint: `${endpoints.LookupVehicleByYmms.url}/${year}/${make}/${model}/${style}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
lookupVehicleByVin(context, { payload: { vin }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.LookupVehicleByVin.method,
endpoint: endpoints.LookupVehicleByVin.url,
payload: {
vin: vin, // EX "1J4GW58S4XC541166"
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
lookupVinByPlate(context, { payload: { licensePlate, licenseState }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.LookupVinByPlate.method,
endpoint: endpoints.LookupVinByPlate.url,
payload: {
licensePlate: licensePlate,
licenseState: licenseState,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
lookupVinByAddress(
context,
{
payload: { licenseLastName, licenseStreetAddress, licenseZip, licenseState },
pageNameToLog,
}
) {
return globalMethods.callHttpClient({
method: endpoints.LookupVinByAddress.method,
endpoint: endpoints.LookupVinByAddress.url,
payload: {
licenseLastName: licenseLastName,
licenseStreetAddress: licenseStreetAddress,
licenseZip: licenseZip,
licenseState: licenseState,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
lookupVinByImage(context, { payload, pageNameToLog }) {
const image = payload;
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = (e) => {
resolve(reader.result);
};
reader.readAsDataURL(image);
}).then((result) => {
const components = result.split(",");
const contentType = image.type;
const imageBase64 = components[1];
const data = {
imageData: imageBase64,
contentType: contentType,
fileName: image.name,
};
return globalMethods.callHttpClient({
method: endpoints.LookupVinByImage.method,
endpoint: endpoints.LookupVinByImage.url,
payload: data,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
});
},
isVinByAddressPermissible(context, { payload: { zip }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.IsVinByAddressPermissible.method,
endpoint: `${endpoints.IsVinByAddressPermissible.url}?zipcode=${zip}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getVehicleMakes(context, { payload: { year }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleMakes.method,
endpoint: `${endpoints.GetVehicleMakes.url}/${year}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getVehicleModels(context, { payload: { year, make }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleModels.method,
endpoint: `${endpoints.GetVehicleModels.url}/${year}/${make}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getVehicleStyles(context, { payload: { year, make, model }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleStyles.method,
endpoint: `${endpoints.GetVehicleStyles.url}/${year}/${make}/${model}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getVehicle(context, { payload: { year, make, model, style }, pageNameToLog }) {
return globalMethods
.callHttpClient({
methods: endpoints.GetVehicle.method,
endpoint: `${endpoints.GetVehicle.url}/${year}/${make}/${model}/${style}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
})
.then((response) => {
return response;
});
},
getDamageOptions(context, { payload: { carId }, pageNameToLog }) {
return globalMethods.callHttpClient({
methods: endpoints.GetDamageOptions.method,
endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
payload: {},
additionalSuccessEventDataHandler: (response) =>
"QueryStringZip: " + getQuerystringParameter(queryStrings.ZIP_CODE),
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
validateZip(context, { payload: { zip }, pageNameToLog }) {
return globalMethods.callHttpClient({
methods: endpoints.ValidateZip.method,
endpoint: `${endpoints.ValidateZip.url}/${zip}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
// Dependency Actions
resetDamageAndDependencies(context) {
context.commit(storeMutations.RESET_DAMAGE_STATE);
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_VAPS, null);
},
resetRegistrationAndDependencies(context) {
context.commit(storeMutations.RESET_REGISTRATION_STATE);
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
},
resetPartsAndDependencies(context) {
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
},
resetServiceLocationAndDependencies(context) {
context.commit(storeMutations.RESET_SERVICE_LOCATION_APPOINTMENT_TYPE);
context.commit(storeMutations.RESET_SERVICE_LOCATION_PROVIDER);
context.commit(storeMutations.RESET_SCHEDULE);
context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
},
resetState(context) {
context.commit(storeMutations.RESET_STATE);
},
resetSaveSessionPromise(context) {
context.commit(storeMutations.RESET_SAVE_SESSION_PROMISE);
},
// Content API Actions
getRouteInfo(context, { pageName }) {
return globalMethods.callHttpClient({
method: endpoints.GetRouteInfo.method,
endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION),
payload: {
pageName: pageName,
},
logApiCall: true,
pageNameToLog: pageName,
});
},
getHomepageName(context) {
return globalMethods.callHttpClient({
method: endpoints.GetHomepageInfo.method,
endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION),
});
},
getPageData(context, { pageName }) {
return globalMethods.callHttpClient({
method: endpoints.GetPageData.method,
endpoint: endpoints.GetPageData.url(
applicationConfig.APPLICATION_ABBREVIATION,
pageName
),
payload: {},
logApiCall: true,
pageNameToLog: pageName,
});
},
// Analytics Actions
logExperimentExposure(
context,
{ payload: { userId, sessionKey, pageName, experiment }, pageNameToLog }
) {
return globalMethods.callHttpClient({
method: endpoints.LogExperimentExposureIfAssigned.method,
endpoint: endpoints.LogExperimentExposureIfAssigned.url,
payload: {
experimentForLogging: {
userId: userId,
experimentUniverseId: experiment.universeId,
experimentUniverseName: experiment.universeName,
experimentTestId: experiment.testId,
experimentTestName: experiment.testName,
experimentVariationId: experiment.variationId,
experimentVariationName: experiment.variationName,
enabled: experiment.isActive,
isExposed: experiment.isExposed,
userPartitionNumber: experiment.userPartitionNumber,
assignmentId: experiment.assignmentId,
sessionKey: sessionKey,
pageName: pageName,
},
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
// Location API Actions
getAlertReasonsByCtu(context, { payload: { ctu }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.GetAlertReasons.method,
endpoint: `${endpoints.GetAlertReasons.url}/${ctu}`,
payload: {},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
// Misc Actions
updateStoreWithSaveSessionResponse(
context,
{
referralNumber,
referralSequenceNumber,
referralDate,
referralCorrelationId,
eon,
parentAccountNumber,
savedSessionId,
crmCustomerId,
workOrderNumber,
customerPortalLoginToken,
}
) {
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
context.commit(storeMutations.UPDATE_REFERRAL_SEQUENCE_NUMBER, referralSequenceNumber);
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
context.commit(storeMutations.UPDATE_EON, eon);
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
context.commit(storeMutations.WORK_ORDER_NUMBER, workOrderNumber);
context.commit(storeMutations.Customer_Portal_Login_Token, customerPortalLoginToken);
},
logPageView(
context,
{
userId,
sessionKey,
pageName,
sessionId,
action,
event,
shouldUseSessionId,
experimentsForUser,
referralSequenceNumber,
parentAccountNumber,
}
) {
var payload = {
userId: userId,
sessionKey: sessionKey,
sessionId: sessionId,
pageName: pageName,
applicationName: applicationConfig.APPLICATION_NAME,
action: action,
event: event,
shouldUseSessionId: shouldUseSessionId,
experimentsForUser: experimentsForUser,
referralSequenceNumber: referralSequenceNumber,
parentAccountNumber: parentAccountNumber,
};
return globalMethods
.callHttpClient({
method: endpoints.LogPageView.method,
endpoint: endpoints.LogPageView.url,
payload: payload,
logApiCall: false,
})
.then(
(response) => {
return response;
},
(error) => {
console.log("Analytics Service Error: " + error.data);
}
);
},
logCustomEvent(
context,
{
userId,
sessionKey,
pageName,
sessionId,
category,
action,
label,
value,
shouldUseSessionId,
experimentsForUser,
referralSequenceNumber,
parentAccountNumber,
}
) {
var payload = {
userId: userId,
sessionKey: sessionKey,
sessionId: sessionId,
pageName: pageName,
applicationName: applicationConfig.APPLICATION_NAME,
category: category,
action: action,
label: label,
value: value,
shouldUseSessionId: shouldUseSessionId,
experimentsForUser: experimentsForUser,
referralSequenceNumber: referralSequenceNumber,
parentAccountNumber: parentAccountNumber,
};
return globalMethods
.callHttpClient({
method: endpoints.LogCustomEvent.method,
endpoint: endpoints.LogCustomEvent.url,
payload: payload,
logApiCall: false,
})
.then(
(response) => {
return response;
},
(error) => {
console.log("Analytics Service Error: " + error.data);
}
);
},
initializeSession(context, { userId, sessionId, userAgent, referrer }) {
var payload = {
applicationName: applicationConfig.APPLICATION_NAME,
userId: userId,
deviceId: userId,
sessionId: sessionId,
userAgent: userAgent,
operatorId: "WEB",
userName: "SafeliteConceptFunnel",
referrer: referrer,
};
return globalMethods
.callHttpClient({
method: endpoints.InitializeSession.method,
endpoint: endpoints.InitializeSession.url,
payload: payload,
logApiCall: false,
})
.then(
(response) => {
return response;
},
(error) => {
console.log("Analytics Service Error: " + error.data);
}
);
},
// Misc Actions
setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) {
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
context.commit(storeMutations.UPDATE_EON, eon);
},
GetExperimentsByUser(context, { userId }) {
return globalMethods.callHttpClient({
method: endpoints.GetExperimentsByUser.method,
endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`,
payload: {},
});
},
async runExperimentsForTrigger(
context,
{ payload: { userId, triggerEvent, triggerValue }, pageNameToLog }
) {
if (triggerEvent == experimentTriggers.SITE_ENTRY) {
context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true);
}
var payload = {
applicationName: applicationConfig.APPLICATION_NAME,
userId: userId,
triggerEvent: triggerEvent,
triggerValue: triggerValue,
experimentOrder: context.getters.experimentOrder,
};
const response = await globalMethods.callHttpClient({
method: endpoints.RunExperimentsForTrigger.method,
endpoint: endpoints.RunExperimentsForTrigger.url,
payload: payload,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
context.commit(storeMutations.UPDATE_EXPERIMENTS, response.data.experiments);
},
getEvoxImage(context, { relativeUrl }) {
return globalMethods.callHttpClient({
method: endpoints.GetPageData.method,
endpoint: relativeUrl,
payload: {},
});
},
// PartsOrQuestions API Actions
async getPartsOrQuestions(context, { pageNameToLog }) {
const vehicle = context.getters.vehicle;
const damage = context.getters.damage;
const order = context.state.order;
const carId = vehicle.carId;
const glassArray = damage.glassToReplace;
const zipCode = order.serviceLocation.zipCode;
const vin = vehicle.vin;
// create a new array to avoid mutating state
const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);
const response = await globalMethods.callHttpClient({
method: endpoints.GetPartsOrQuestions.method,
endpoint: endpoints.GetPartsOrQuestions.url,
payload: {
carId: carId,
glassPieces: glassArrayForPayload,
zip: zipCode,
vin: vin,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
// Flatten location and name properties
response.data.partsOrQuestions = convertGlassPieceNamingFromApi(
response.data.partsOrQuestions
);
return response;
},
// Parts API Actions
async getParts(context, { pageNameToLog }) {
const vehicle = context.getters.vehicle;
const damage = context.getters.damage;
const order = context.state.order;
const carId = vehicle.carId;
const glassArray = damage.glassToReplace;
const resultsArray = damage.partQuestionAnswers;
const zipCode = order.serviceLocation.zipCode;
const vin = vehicle.vin;
// create a new array to avoid mutating state
const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);
const resultsArrayForPayload = convertResultsForApi(resultsArray);
const response = await globalMethods.callHttpClient({
method: endpoints.GetParts.method,
endpoint: endpoints.GetParts.url,
payload: {
carId: carId,
glassPieces: glassArrayForPayload,
answerResults: resultsArrayForPayload,
zip: zipCode,
vin: vin,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
// Flatten location and name properties
response.data.glassPieceParts = convertGlassPieceNamingFromApi(
response.data.glassPieceParts
);
return response;
},
async getWipers(context, { pageNameToLog }) {
const carId = context.getters.vehicle.carId;
const serviceZipCode = context.getters.order.serviceLocation.zipCode;
const response = await globalMethods
.callHttpClient({
method: endpoints.GetWipers.method,
endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
})
.catch((error) => {
// The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow
return [];
});
syncLineItemIds(response.data, context.getters.order.lineItems.vaps);
return response;
},
async getRainDefense(context, { pageNameToLog }) {
const response = await globalMethods.callHttpClient({
method: endpoints.GetRainDefense.method,
endpoint: endpoints.GetRainDefense.url,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
syncLineItemIds([response.data], context.getters.order.lineItems.vaps);
return response;
},
getMobileFeePart(context, { pageNameToLog }) {
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
const parentAccountNumber = context.getters.payment.parentAccountNumber;
const billToAccountNumber = 87291; // TODO: MAKE THIS REAL
return globalMethods.callHttpClient({
method: endpoints.GetMobileFeePart.method,
endpoint: `${endpoints.GetMobileFeePart.url}/${damageType}/${parentAccountNumber}/${billToAccountNumber}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getServiceabilityDetails(context, { payload: { serviceZipCode }, pageNameToLog }) {
const lineItemsWithOnlyPartNumbers = context.getters.order.lineItems.supportingItems.map(
(lineItem) => ({
partNumber: lineItem.partNumber,
})
);
const lineItems = buildQueryStringParameterFromArrayOfComplexObjects(
lineItemsWithOnlyPartNumbers,
"lineItems"
);
const vehicle = context.getters.vehicle;
const carId = vehicle.carId;
const damage = context.getters.damage;
const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace);
const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects(
glassArray,
"glassPieces"
);
return globalMethods.callHttpClient({
method: endpoints.GetServiceabilityDetails.method,
endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&${lineItems}&${glassPieces}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getProviders(context, { payload: { serviceZipCode }, pageNameToLog }) {
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
const shopRadiusInMiles = 100;
return globalMethods.callHttpClient({
method: endpoints.GetProviders.method,
endpoint: `${endpoints.GetProviders.url}/${serviceZipCode}/${damageType}/${shopRadiusInMiles}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getSupportingItems(context, { pageNameToLog }) {
const glassPartsArray = context.getters.lineItems.glassParts ?? [];
const carId = context.getters.vehicle.carId;
const isRepair = context.getters.damage.isRepair;
const numberOfChips = context.getters.damage.numberOfChips;
return globalMethods.callHttpClient({
method: endpoints.GetSupportingItems.method,
endpoint: endpoints.GetSupportingItems.url,
payload: {
carId: carId,
damageType: isRepair ? "Repair" : "Replace",
parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER,
parts: glassPartsArray,
numberOfRepairChips: isRepair ? numberOfChips : 0,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getCapabilityQuestions(context, { payload: { carId, partNumber }, pageNameToLog }) {
return globalMethods.callHttpClient({
method: endpoints.GetCapabilityQuestions.method,
endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getPartFromCapabilityQuestionAnswer(context, { payload, pageNameToLog }) {
const glassLocation = payload;
const pageData = context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
const part = pageData.partsOrQuestions.find((x) => x.glassLocation === glassLocation)
.parts[0];
const capabilityQuestionAnswers = context.getters.damage.capabilityQuestionAnswers;
const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find(
(x) => x.glassLocation === glassLocation
);
return globalMethods.callHttpClient({
method: endpoints.GetPartFromCapabilityAnswer.method,
endpoint: endpoints.GetPartFromCapabilityAnswer.url,
payload: {
part,
capabilityAnswerResults: capabilityQuestionAnswersForPart,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
getShopTimeSlots(
context,
{ payload: { startDate, endDate, shopAppointmentType, providerNumber }, pageNameToLog }
) {
const order = context.state.order;
const vehicle = context.state.order.vehicle;
let lineItems = [
...(order.lineItems.supportingItems ?? []),
...(order.lineItems.vaps ?? []),
...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts),
];
lineItems = lineItems.map((lineItem) => {
return {
partNumber: lineItem.partNumber,
partType: lineItem.partType,
};
});
const glassPieces = order.damage.glassToReplace
? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
: [];
var payload = {
zipCode: order.serviceLocation.provider.zipCode,
providerNumber: providerNumber,
startDate: startDate,
endDate: endDate,
shopAppointmentType: shopAppointmentType,
applicationName: applicationConfig.APPLICATION_NAME,
parentAccountNumber: context.getters.payment.parentAccountNumber,
carId: vehicle.carId,
lineItems: lineItems,
glassPieces: glassPieces,
eon: order.eon,
coverage: {
status: "",
deductible: 0,
additionalAuthFlag: "",
},
partSelection: {
hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
hasManuallySelectedParts:
!!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
.length,
},
vehicle: {
year: vehicle.year,
make: vehicle.make,
model: vehicle.model,
style: vehicle.style,
vin: vehicle.vin ?? "",
},
};
return globalMethods.callHttpClient({
method: endpoints.GetShopTimeSlots.method,
endpoint: endpoints.GetShopTimeSlots.url,
payload: payload,
logApiCall: true,
pageNameToLog: pageNameToLog,
additionalSuccessEventDataHandler: (response) =>
getTimeSlotsAdditionalEventData(
response.data.provisionalTriggers,
order.serviceLocation.zipCode,
response.data.days?.[0]?.date,
shopAppointmentType
),
});
},
getMobileTimeSlots(context, { payload: { startDate, endDate }, pageNameToLog }) {
const order = context.state.order;
const vehicle = context.state.order.vehicle;
let lineItems = [
...(order.lineItems.supportingItems ?? []),
...(order.lineItems.vaps ?? []),
...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts),
];
lineItems = lineItems.map((lineItem) => {
return {
partNumber: lineItem.partNumber,
partType: lineItem.partType,
};
});
const glassPieces = order.damage.glassToReplace
? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
: [];
var payload = {
startDate: startDate,
endDate: endDate,
applicationName: applicationConfig.APPLICATION_NAME,
parentAccountNumber: context.getters.payment.parentAccountNumber,
carId: vehicle.carId,
lineItems: lineItems,
glassPieces: glassPieces,
eon: order.eon,
coverage: {
status: "",
deductible: 0,
additionalAuthFlag: "",
},
partSelection: {
hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
hasManuallySelectedParts:
!!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
.length,
},
vehicle: {
year: vehicle.year,
make: vehicle.make,
model: vehicle.model,
style: vehicle.style,
vin: vehicle.vin ?? "",
},
zipCode: order.serviceLocation.zipCode,
};
return globalMethods.callHttpClient({
method: endpoints.GetMobileTimeSlots.method,
endpoint: endpoints.GetMobileTimeSlots.url,
payload: payload,
logApiCall: true,
pageNameToLog: pageNameToLog,
additionalSuccessEventDataHandler: (response) =>
getTimeSlotsAdditionalEventData(
response.data.provisionalTriggers,
order.serviceLocation.zipCode,
response.data.days?.[0]?.date
),
});
},
getMobilePremiumFee(context, { pageNameToLog }) {
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash";
return globalMethods.callHttpClient({
method: endpoints.GetMobilePremiumFee.method,
endpoint: `${endpoints.GetMobilePremiumFee.url}/${paymentType}/${damageType}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
},
// Signing Service for Payment
getSignature() {
return globalMethods.callHttpClient({
method: endpoints.GetSignature.method,
endpoint: endpoints.GetSignature.url,
});
},
// Session API Actions
saveSession(context, { pageNameToLog, payload }) {
const vehicle = context.getters.vehicle;
const damage = context.getters.damage;
const order = context.state.order;
const applicationUser = context.getters.applicationUser;
const lineItems = context.state.order.lineItems;
const submitAfterSave = payload?.submitAfterSave === "true";
const createDeleteStatusWorkOrderForPia =
payload?.createDeleteStatusWorkOrderForPia === "true";
// create a new array to avoid mutating state
const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace);
return globalMethods.callHttpClient({
method: endpoints.SaveSession.method,
endpoint: endpoints.SaveSession.url,
payload: {
submitAfterSave: submitAfterSave,
userAgent: navigator.userAgent,
applicationUser: {
crmCustomerId: applicationUser.crmCustomerId,
experiments: applicationUser.experiments,
lastPage: applicationUser.lastPageVisited,
pageData: applicationUser.pageData,
savedSessionId: applicationUser.savedSessionId,
},
order: {
vehicle: {
carId: vehicle.carId,
year: vehicle.year,
make: vehicle.make,
model: vehicle.model,
style: vehicle.style,
vin: vehicle.vin,
registration: {
firstName: vehicle.registration.firstName,
lastName: vehicle.registration.lastName,
streetAddress: vehicle.registration.address,
city: vehicle.registration.city,
state: vehicle.registration.state,
zipCode: vehicle.registration.zipCode,
licensePlateNumber: vehicle.registration.licensePlate,
},
},
customer: {
emailAddress: order.customer.emailAddress,
firstName: order.customer.firstName,
lastName: order.customer.lastName,
isSmsOptIn: order.customer.isSmsOptIn,
phoneNumber: order.customer.phoneNumber,
},
damage: {
numberOfChips: damage.numberOfChips,
glassToReplace: newGlassToReplace,
isRepair: damage.isRepair,
partQuestionAnswers: order.damage.partQuestionAnswers,
moldingQuestionAnswers: order.damage.moldingQuestionAnswers,
capabilityQuestionAnswers: order.damage.capabilityQuestionAnswers,
},
lineItems: {
glassParts: lineItems.glassParts,
supportingItems: lineItems.supportingItems,
vaps: lineItems.vaps,
serverData: lineItems.serverData,
promos: lineItems.promos,
},
payment: {
InsuranceCoverage: {
isVerified: order.payment.insuranceCoverage.isVerified ?? false,
},
isInsurance: order.payment.isInsurance,
parentAccountNumber: order.payment.parentAccountNumber,
inactivePromos: order.payment.inactivePromos,
isCreditCard:
order.payment.piaType == paymentMethods.CREDIT_CARD ? true : false,
isPaypal: order.payment.piaType == paymentMethods.PAYPAL ? true : false,
isAfterPay: order.payment.piaType == paymentMethods.AFTERPAY ? true : false,
paypalToken: order.payment.paypalToken,
ccToken: {
subscriptionId: order.payment.ccToken.subscriptionId,
expMonth: order.payment.ccToken.expMonth,
expYear: order.payment.ccToken.expYear,
cardType: order.payment.ccToken.cardType,
billToPostalCode: order.payment.ccToken.billToPostalCode,
billToFirstName: order.payment.ccToken.billToFirstName,
billToLastName: order.payment.ccToken.billToLastName,
referenceNumber: order.payment.ccToken.referenceNumber,
authCode: order.payment.ccToken.authCode,
transactionId: order.payment.ccToken.transactionId,
transReferenceNumber: order.payment.ccToken.transReferenceNumber,
},
},
serviceLocation: {
streetAddress: order.serviceLocation.address,
streetAddress2: order.serviceLocation.address2,
city: order.serviceLocation.city,
state: order.serviceLocation.state,
zipCode: order.serviceLocation.zipCode,
zipCodeCtu: order.serviceLocation.zipCodeCtu,
appointmentType: order.serviceLocation.appointmentType,
isVehicleProtected: order.serviceLocation.isVehicleProtected,
provider: {
providerNumber: order.serviceLocation.provider?.providerNumber,
address: {
streetAddress:
order.serviceLocation.provider?.address?.streetAddress,
city: order.serviceLocation.provider?.address?.city,
state: order.serviceLocation.provider?.address?.state,
zipCode: order.serviceLocation.provider?.address?.zipCode,
zipCodeCtu: order.serviceLocation.provider?.address?.zipCodeCtu,
},
},
techNotes: order.serviceLocation.techNotes,
},
schedule: {
date: order.schedule?.date,
startTime: order.schedule?.startTime,
endTime: order.schedule?.endTime,
routeCode: order.schedule?.routeCode,
jobMaxMinutes: order.schedule?.jobMaxMinutes,
jobMinMinutes: order.schedule?.jobMinMinutes,
},
existingPromoCode: null,
referralCorrelationId: order.referralCorrelationId,
referralDate: order.referralDate,
referralNumber: order.referralNumber?.toString(),
referralSequenceNumber: order.referralSequenceNumber,
eon: order.eon,
createDeleteStatusWorkOrderForPia: createDeleteStatusWorkOrderForPia,
},
},
logApiCall: true,
pageNameToLog: pageNameToLog,
additionalSuccessEventDataHandler: (response) =>
"Email provided: " + (order.customer.emailAddress ? "true" : "false"),
});
},
loadSession(
context,
{
payload: {
savedSessionId,
referralNumber,
referralDate,
parentAccountNumber,
referralCorrelationId,
isConceptInsurance,
},
pageNameToLog,
}
) {
const order = context.state.order;
return globalMethods
.callHttpClient({
method: endpoints.LoadSession.method,
endpoint: endpoints.LoadSession.url,
payload: {
savedSessionId: savedSessionId?.toString(),
referralNumber: referralNumber?.toString(),
referralDate: referralDate?.toString(),
parentAccountNumber: parentAccountNumber,
referralCorrelationId: referralCorrelationId,
},
logApiCall: true,
pageNameToLog: pageNameToLog,
})
.then(
async (response) => {
// Flatten location and name properties
response.data.order.damage?.glassToReplace?.map((glass) => {
glass.glassLocation = glass.location;
glass.glassName = glass.name;
delete glass.location;
delete glass.name;
return glass;
});
// clear the state if the existing EON does not equal what is returned from loadSession
if (context.state.order.eon && context.state.order.eon != response.data.eon) {
context.commit(storeMutations.RESET_STATE);
}
context.commit(
storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION,
response.data
);
await resetScheduleIfUnavailable(context, response.data.order, pageNameToLog);
return response;
},
(error) => {
deleteFunnelCookie();
context.commit(storeMutations.RESET_STATE);
}
);
},
saveCCToken(context, ccToken) {
context.commit(storeMutations.UPDATE_CCTOKEN, ccToken);
},
savePaypalToken(context, ppToken) {
context.commit(storeMutations.UPDATE_PAYPAL_TOKEN, ppToken);
},
// Business domain actions
// Vehicle
saveVehicle(
context,
{ year, make, model, style, carId, category, imageUrl, imageVifNumber, imageVifColor }
) {
if (
context.state.order.vehicle.year != year ||
context.state.order.vehicle.make != make ||
context.state.order.vehicle.model != model ||
context.state.order.vehicle.style != style
) {
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
context.commit(storeMutations.UPDATE_YEAR, year);
context.commit(storeMutations.UPDATE_MAKE, make);
context.commit(storeMutations.UPDATE_MODEL, model);
context.commit(storeMutations.UPDATE_STYLE, style);
context.commit(storeMutations.UPDATE_CAR_ID, carId);
context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, category);
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, imageUrl);
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, imageVifNumber);
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, imageVifColor);
}
},
saveVehicleDamage(
context,
{ isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount }
) {
const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort();
const isGlassToReplaceTheSame =
context.state.order.damage.glassToReplace?.length === selectedGlassToReplace.length &&
context.state.order.damage.glassToReplace
.slice()
.sort()
.every(
(obj, index) =>
obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation &&
obj.glassName === selectedGlassPassedInSorted[index].glassName
);
const isWindshieldRepairTheSame =
isWindshieldRepair === context.state.order.damage.isRepair;
const isChipCountTheSame =
selectedWindshieldChipCount === context.state.order.damage.numberOfChips;
const isDamageChanging =
!isGlassToReplaceTheSame ||
!isWindshieldRepairTheSame ||
(isWindshieldRepair && !isChipCountTheSame);
if (isDamageChanging) {
//Reset dependent state when changing
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_VAPS, null);
// Save new values
context.commit(storeMutations.UPDATE_IS_REPAIR, isWindshieldRepair);
context.commit(
storeMutations.UPDATE_NUMBER_OF_CHIPS,
isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null
);
context.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, selectedGlassToReplace);
}
},
// Vin lookup
saveVinLookup(context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) {
//Reset dependent state when changing
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
if (!isSelectedGlassAvailableForVehicle) {
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
}
//Save new values
context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
}
},
saveRegistrationLicensePlateLookup(
context,
{ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }
) {
//Reset dependent state when changing
if (
registrationInfo?.licensePlate !==
context.state.order.vehicle.registration?.licensePlate
) {
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
if (!isSelectedGlassAvailableForVehicle) {
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
}
context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
if (registrationInfo) {
context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
// only update name information if there isn't a value in state already
if (!context.getters.order.customer.firstName) {
context.commit(storeMutations.UPDATE_CUSTOMER_DETAILS, {
firstName: registrationInfo.firstName,
lastName: registrationInfo.lastName,
});
}
}
}
},
saveRegistrationAddressLookup(
context,
{ isSelectedGlassAvailableForVehicle, vehicleInfo, customerInfo }
) {
//Reset dependent state when changing
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
if (!isSelectedGlassAvailableForVehicle) {
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
}
}
context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
// only update name information if there isn't a value in state already
if (!context.getters.order.customer.firstName && customerInfo) {
context.commit(storeMutations.UPDATE_CUSTOMER_DETAILS, {
firstName: customerInfo.firstName,
lastName: customerInfo.lastName,
});
}
},
savePartQuestionAnswers(context, partQuestionAnswersArray) {
// if part question answers have changed, reset subsequent question answers
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
context.getters.damage.partQuestionAnswers,
"result"
);
const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
partQuestionAnswersArray,
"result"
);
const havePartQuestionAnswersChanged =
sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length ||
!sortedPreviousResultsArray?.every(
(x, i) => x.result === sortedPartQuestionAnswersArray[i].result
);
if (havePartQuestionAnswersChanged) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
context.commit(storeMutations.UPDATE_PAGE_DATA, {
page: fmgPageValues.VEHICLE_PARTS,
data: null,
});
context.commit(storeMutations.UPDATE_PAGE_DATA, {
page: fmgPageValues.MOLDING_QUESTIONS,
data: null,
});
context.commit(storeMutations.UPDATE_PAGE_DATA, {
page: fmgPageValues.CAPABILITY_QUESTIONS,
data: null,
});
}
//Save new values
context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray);
},
resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) {
const partsOrQuestionsDataToCompareWith =
context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ??
context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ??
[];
function getAllPartNumbers(partsOrQuestions) {
return partsOrQuestions[0]?.parts
? [...partsOrQuestions]
.map((glass) => glass.parts)
.flat()
.map((part) => part.partNumber)
.filter((partNumber) => !partNumber.toUpperCase().includes("FEE"))
.sort()
.join(",")
: [];
}
const previouslySelectedPartNumbers = getAllPartNumbers(partsOrQuestionsDataToCompareWith);
const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts);
const haveSelectedVehiclePartsChanged =
previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
if (haveSelectedVehiclePartsChanged) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
context.commit(storeMutations.UPDATE_PAGE_DATA, {
page: fmgPageValues.MOLDING_QUESTIONS,
data: null,
});
context.commit(storeMutations.UPDATE_PAGE_DATA, {
page: fmgPageValues.CAPABILITY_QUESTIONS,
data: null,
});
}
},
saveMoldingQuestionAnswers(context, moldingQuestionAnswers) {
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
context.getters.damage.moldingQuestionAnswers,
"partNum"
);
const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
moldingQuestionAnswers,
"partNum"
);
const haveMoldingQuestionAnswersChanged =
sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length ||
!sortedPreviousResultsArray?.every(
(x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum
);
if (haveMoldingQuestionAnswersChanged) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
context.commit(storeMutations.UPDATE_PAGE_DATA, {
page: fmgPageValues.CAPABILITY_QUESTIONS,
data: null,
});
}
//Save new values
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers);
},
saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) {
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
context.getters.damage.capabilityQuestionAnswers,
"result"
);
const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
capabilityQuestionAnswers,
"result"
);
const haveCapabilityQuestionAnswersChanged =
sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length ||
!sortedPreviousResultsArray?.every(
(x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result
);
if (haveCapabilityQuestionAnswersChanged) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null);
}
//Save new values
context.commit(
storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS,
capabilityQuestionAnswers
);
},
savePaymentType(context, isInsurance) {
if (isInsurance !== context.getters.payment.isInsurance) {
context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
}
context.commit(storeMutations.UPDATE_IS_INSURANCE, isInsurance);
},
savePaymentMethodChoice(context, paymentMethod) {
const isPia = paymentMethod !== paymentMethods.LATER;
context.commit(storeMutations.UPDATE_IS_PIA, isPia);
context.commit(storeMutations.UPDATE_PIA_TYPE, isPia ? paymentMethod : null);
},
saveParentAccountNumber(context, parentAccountNumber) {
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber);
},
saveSupportingItems(context, supportingItems) {
if (!deepEqual(supportingItems, context.state.order.lineItems.supportingItems)) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
}
addGuidToLineItemsIfNotAlreadyThere(supportingItems);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
},
saveSupportingItemsSuppressingStateResetting(context, supportingItems) {
addGuidToLineItemsIfNotAlreadyThere(supportingItems);
context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems);
},
saveVaps(context, vaps) {
addGuidToLineItemsIfNotAlreadyThere(vaps);
context.commit(storeMutations.UPDATE_VAPS, vaps);
},
savePromos(context, promos) {
context.commit(storeMutations.UPDATE_PROMOS, promos);
},
// Price order actions
async priceOrderItemsAndSaveServerData(
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);
const lineItemsWithOnlyPartNumbers = flattenedLineItemsWithChildParts.map((lineItem) => ({
partNumber: lineItem.partNumber,
}));
const availableLineItemsFormattedForRequest =
buildQueryStringParameterFromArrayOfComplexObjects(
lineItemsWithOnlyPartNumbers,
"lineItems"
);
const vehicle = context.getters.order.vehicle;
let queryString =
`ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` +
`&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)}`;
}
const response = await globalMethods.callHttpClient({
method: endpoints.PriceOrderItems.method,
endpoint: `${endpoints.PriceOrderItems.url}?${queryString}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData);
availableLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems);
return availableLineItems;
},
// Tax order actions
async taxOrderItemsAndSaveServerData(
context,
{
payload: {
billToAccountNumber,
providerNumber,
appointmentType,
serviceLocationCity,
serviceLocationState,
serviceLocationZipCode,
pricedAvailableLineItems,
},
pageNameToLog,
}
) {
const flattenedLineItemsWithChildParts =
getFlattenedArrayOfLineItemsWithChildParts(pricedAvailableLineItems);
const lineItemsWithOnlyPriceInfo = flattenedLineItemsWithChildParts.map((lineItem) => ({
partNumber: lineItem.partNumber,
laborAmount: lineItem.laborAmount ?? 0,
kitPrice: lineItem.kitPrice ?? 0,
sellingPrice: lineItem.sellingPrice ?? 0,
}));
const pricedLineItemsFormattedForRequest =
buildQueryStringParameterFromArrayOfComplexObjects(
lineItemsWithOnlyPriceInfo,
"lineItems"
);
let queryString = "";
if (appointmentType == "Mobile") {
queryString =
`ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` +
`&BillToAccountNumber=${billToAccountNumber}` +
`&ProviderNumber=${providerNumber}` +
`&AppointmentType=${appointmentType}` +
`&ServiceLocation.City=${serviceLocationCity}` +
`&ServiceLocation.State=${serviceLocationState}` +
`&ServiceLocation.ZipCode=${serviceLocationZipCode}` +
`&${pricedLineItemsFormattedForRequest}`;
} else {
queryString =
`ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` +
`&BillToAccountNumber=${billToAccountNumber}` +
`&ProviderNumber=${providerNumber}` +
`&AppointmentType=${appointmentType}` +
`&${pricedLineItemsFormattedForRequest}`;
}
const lineItemServerData = context.getters.order.lineItems.serverData;
if (lineItemServerData) {
queryString += `&ServerData=${encodeURIComponent(lineItemServerData)}`;
}
const response = await globalMethods.callHttpClient({
method: endpoints.TaxOrderItems.method,
endpoint: `${endpoints.TaxOrderItems.url}?${queryString}`,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData);
pricedAvailableLineItems = addTaxesToPricedLineItems(
pricedAvailableLineItems,
response.data.taxedLineItems
);
return pricedAvailableLineItems;
},
async validateOrderPromoAndSaveServerData(
context,
{ payload: { promoCode, addableVaps }, pageNameToLog }
) {
const order = context.getters.order;
addGuidToLineItemsIfNotAlreadyThere(addableVaps);
const requestObject = {
promoCode: promoCode,
addableVaps: addableVaps,
order: {
appointmentType: order.serviceLocation.appointmentType,
carId: order.vehicle.carId,
correlationId: order.referralCorrelationId,
eon: order.eon,
isRepair: order.damage.isRepair,
glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
lineItemsOnOrder: getArrayOfAllLineItems(order.lineItems),
parentAccountNumber: order.payment.parentAccountNumber,
referralSequenceNumber: order.referralSequenceNumber,
serviceState: order.serviceLocation.state,
serverData: order.lineItems.serverData,
vehicleYear: "" + order.vehicle.year,
zipCodeOrProviderCtu:
order.serviceLocation.appointmentType === "Inshop"
? order.serviceLocation.provider.address.zipCodeCtu
: order.serviceLocation.zipCodeCtu,
},
};
const validateResponse = await globalMethods.callHttpClient({
method: endpoints.ValidatePromo.method,
endpoint: endpoints.ValidatePromo.url,
payload: requestObject,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
if (validateResponse?.data?.serverData) {
context.commit(
storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA,
validateResponse.data.serverData
);
}
return validateResponse;
},
async revalidateOrderPromosAndUpdateStore(context, { pageNameToLog }) {
const order = context.getters.order;
let requestObject = {
inactivePromos: order.payment.inactivePromos,
order: {
appointmentType: order.serviceLocation.appointmentType,
carId: order.vehicle.carId,
correlationId: order.referralCorrelationId,
eon: order.eon,
isRepair: order.damage.isRepair,
glassToReplace: renameGlassToReplaceAttributes(order.damage.glassToReplace),
lineItemsOnOrder: getArrayOfAllLineItems(order.lineItems),
parentAccountNumber: order.payment.parentAccountNumber,
referralSequenceNumber: order.referralSequenceNumber,
serviceState: order.serviceLocation.state,
serverData: order.lineItems.serverData,
vehicleYear: "" + order.vehicle.year,
zipCodeOrProviderCtu:
order.serviceLocation.appointmentType === "Inshop"
? order.serviceLocation.provider.address.zipCodeCtu
: order.serviceLocation.zipCodeCtu,
},
};
const revalidateResponse = await globalMethods.callHttpClient({
method: endpoints.RevalidatePromos.method,
endpoint: endpoints.RevalidatePromos.url,
payload: requestObject,
logApiCall: true,
pageNameToLog: pageNameToLog,
});
const revalidationErrorPromoCodes = revalidateResponse.data.errors.map((x) => x.promoCode);
context.commit(storeMutations.UPDATE_PROMOS, revalidateResponse.data.promoLineItems);
context.commit(storeMutations.UPDATE_INACTIVE_PROMOS, revalidationErrorPromoCodes);
context.commit(
storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA,
revalidateResponse.data.lineItemsServerData
);
return revalidateResponse;
},
// Misc order actions
saveSchedule(context, scheduleInfo) {
context.commit(storeMutations.UPDATE_SCHEDULE, scheduleInfo);
},
saveCustomerDetails(context, customerDetails) {
context.commit(storeMutations.UPDATE_CUSTOMER_DETAILS, customerDetails);
},
saveServiceZipCodeInfo(context, serviceZipCodeInfo) {
if (
context.state.order.serviceLocation &&
serviceZipCodeInfo.zipCode !== context.state.order.serviceLocation.zipCode
) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
context.commit(storeMutations.RESET_SERVICE_LOCATION_MOBILE_ADDRESS);
}
context.commit(storeMutations.UPDATE_SERVICE_ZIP, serviceZipCodeInfo);
},
saveServiceLocation(context, serviceLocationInfo) {
if (context.state.order.serviceLocation) {
if (
serviceLocationInfo.zipCode !== context.state.order.serviceLocation.zipCode ||
!providersEqual(
serviceLocationInfo.provider,
context.state.order.serviceLocation.provider
) ||
serviceLocationInfo.appointmentType !==
context.state.order.serviceLocation.appointmentType
) {
context.commit(storeMutations.RESET_SCHEDULE);
context.commit(storeMutations.RESET_PAYMENT_METHOD_CHOICE);
}
}
context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo);
},
saveServiceLocationTechNotes(context, techNotesInfo) {
context.commit(storeMutations.UPDATE_SERVICE_LOCATION_TECH_NOTES, techNotesInfo);
},
saveEmail(context, email) {
context.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, email === "" ? null : email);
},
saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) {
//Reset dependent state when changing
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
if (!isSelectedGlassAvailableForVehicle) {
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
}
//Save new values
context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo);
}
},
saveGlassParts(context, parts) {
if (!deepEqual(parts, context.state.order.lineItems.glassParts)) {
context.dispatch(storeActions.RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES);
}
addGuidToLineItemsIfNotAlreadyThere(parts);
context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
},
saveGlassPartsSuppressingStateResetting(context, parts) {
context.commit(storeMutations.UPDATE_GLASS_PARTS, parts);
},
clearVin(context) {
context.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
},
isVinOptionalVehicle(context) {
//Optional for carIds with only a single windshield
if (
singleWindshieldCarIds.find((item) => item === context.state.order.vehicle.carId) &&
context.state.order.damage.glassToReplace.length == 1 &&
context.state.order.damage.glassToReplace.find(
(glassToReplace) =>
glassToReplace.glassLocation.toLowerCase() ===
damageLocationsSelected.WINDSHIELD.toLowerCase()
)
) {
return true;
}
//Optional for specific YMMSs
switch (context.state.order.vehicle.make.toLowerCase()) {
case "mercedes benz":
case "volkswagen":
case "audi":
case "porsche":
return true;
default:
}
if (
context.state.order.vehicle.make.toLowerCase() === "bmw" &&
context.state.order.vehicle.year <= 2017
)
return true;
return false;
},
};
export default createStore({
plugins: [createPersistedState()],
// IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons:
// * The CMS can reference the fields by name
// * Return users may have a previous "version" of the model, and we don't want
// them to have a breaking experience, because the model might have changed.
state,
mutations,
getters,
actions,
});
// Private Functions
function getHasRecalibrationPart(state) {
var hasRequiresRecalibration =
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"requiresRecalibration"
)?.length > 0;
var hasRecalibrationType =
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
)?.length > 0;
if (hasRequiresRecalibration) {
if (hasRecalibrationType) {
// Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType'
return (
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
)[0].toLowerCase() != "unknown"
);
} else {
// Has 'requiresRecalibration' but no 'recalibrationType' at all
return true;
}
} else {
// Does not have 'requiresRecalibration'
return false;
}
}
function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
if (!arrayOfObjects) return null;
return arrayOfObjects.sort((a, b) => {
if (a[propertyName] < b[propertyName]) return -1;
else if (a[propertyName] > b[propertyName]) return 1;
else return 0;
});
}
function convertGlassPieceNamingForApi(glassArray) {
if (!glassArray || glassArray.length === 0) return [];
// check if array already converted. (likely when a session has been saved previously and then reloaded)
if (glassArray[0].location !== undefined) {
return glassArray;
}
const converted = [];
glassArray.forEach((glass) => {
converted.push({
location: glass.glassLocation,
name: glass.glassName,
});
});
return converted;
}
function convertResultsForApi(resultsArray) {
if (!resultsArray) return [];
const converted = [];
resultsArray.forEach((answer) => {
converted.push({
location: answer.glassLocation,
name: answer.glassName,
result: answer.result,
});
});
return converted;
}
function convertGlassPieceNamingFromApi(glassArray) {
glassArray.forEach((glass) => {
glass.glassLocation = glass.glassPiece.location;
glass.glassName = glass.glassPiece.name;
delete glass.glassPiece;
return glass;
});
return glassArray;
}
function addPricesToLineItems(lineItems, pricingLineItems) {
lineItems.forEach((lineItem) => {
let lineItemIndex = pricingLineItems.findIndex(
(pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber
);
if (lineItem.childParts) {
addPricesToLineItems(lineItem.childParts, pricingLineItems);
}
let pricedLineItem = pricingLineItems.splice(lineItemIndex, 1)[0];
lineItem.laborAmount = pricedLineItem.laborAmount;
lineItem.sellingPrice = pricedLineItem.sellingPrice;
lineItem.kitPrice = pricedLineItem.kitPrice;
lineItem.salesTax = pricedLineItem.salesTax;
});
return lineItems;
}
function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) {
pricedLineItems.forEach((pricedLineItem) => {
let lineItemIndex = taxingLineItems.findIndex(
(taxingLineItem) => taxingLineItem.partNumber === pricedLineItem.partNumber
);
if (pricedLineItem.childParts) {
addTaxesToPricedLineItems(pricedLineItem.childParts, taxingLineItems);
}
let taxedLineItem = taxingLineItems.splice(lineItemIndex, 1)[0];
pricedLineItem.salesTax = taxedLineItem?.salesTax ?? 0;
});
return pricedLineItems;
}
export class LineItemUtilities {
constructor() {
this.getArrayOfAllLineItems = getArrayOfAllLineItems;
this.getFlattenedArrayOfLineItemsWithChildParts =
getFlattenedArrayOfLineItemsWithChildParts;
this.addGuidToLineItemsIfNotAlreadyThere = addGuidToLineItemsIfNotAlreadyThere;
}
}
export function mapTaxedAvailableLineItemsToStoreFormat(availableLineItems, storeLineItems) {
// clone the lineItems array because what we're passing in is referencing the store directly
const lineItems = deepClone(storeLineItems);
for (let [category, lineItemsInCategory] of Object.entries(lineItems)) {
lineItemsInCategory = lineItemsInCategory ?? [];
for (let lineItemIndex = 0; lineItemIndex < lineItemsInCategory.length; lineItemIndex++) {
const availableLineItem = availableLineItems.find(
(ali) => ali.partNumber == lineItemsInCategory[lineItemIndex].partNumber
);
if (availableLineItem) {
lineItemsInCategory[lineItemIndex].salesTax = availableLineItem.salesTax;
}
}
}
return lineItems;
}
function getArrayOfAllLineItems(lineItems) {
let consolidatedLineItemsArray = [];
if (lineItems.glassParts != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.glassParts];
if (lineItems.supportingItems != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.supportingItems];
if (lineItems.vaps != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.vaps];
if (lineItems.promos != null)
consolidatedLineItemsArray = [...consolidatedLineItemsArray, ...lineItems.promos];
return consolidatedLineItemsArray;
}
function getFlattenedArrayOfLineItemsWithChildParts(lineItems) {
let flattenedArray = [];
lineItems?.forEach((lineItem) => {
flattenedArray.push(lineItem);
if (lineItem.childParts) {
flattenedArray = [
...flattenedArray,
...getFlattenedArrayOfLineItemsWithChildParts(lineItem.childParts),
];
}
});
return flattenedArray;
}
function buildQueryStringParameterFromArrayOfComplexObjects(arrayOfObjects, parameterName) {
let queryStringParameter = "";
for (let i = 0; i < arrayOfObjects.length; i++) {
for (const [key, value] of Object.entries(arrayOfObjects[i])) {
queryStringParameter += `${parameterName}[${i}].${key}=${value}&`;
}
}
// Remove trailing &
return queryStringParameter.slice(0, -1);
}
function convertGlassPieceToBackEndCompatibleFormat(glassPieces) {
return glassPieces.map((glassPiece) => {
return {
location: glassPiece.glassLocation,
name: glassPiece.glassName,
};
});
}
function renameGlassToReplaceAttributes(glassToReplace) {
let newGlassToReplace = [];
if (glassToReplace) {
glassToReplace.forEach((item) => {
newGlassToReplace.push({ location: item.glassLocation, name: item.glassName });
});
}
return newGlassToReplace;
}
function addGuidToLineItemsIfNotAlreadyThere(lineItems) {
lineItems.forEach((lineItem) => {
if (!lineItem.id) {
lineItem.id = crypto.randomUUID();
}
});
}
function syncLineItemIds(lineItemsWithoutIds, lineItemsWithIds) {
if (!lineItemsWithIds || !lineItemsWithoutIds) {
return;
}
lineItemsWithoutIds.forEach((noId) => {
lineItemsWithIds.forEach((withId) => {
if (noId.partNumber === withId.partNumber && noId.partType === withId.partType) {
noId.id = withId.id;
}
});
});
}
function providersEqual(providerA, providerB) {
return (
providerA.providerNumber === providerB.providerNumber &&
providerA.address?.city === providerB.address?.city &&
providerA.address?.state === providerB.address?.state &&
providerA.address?.streetAddress === providerB.address?.streetAddress &&
providerA.address?.zipCode === providerB.address?.zipCode
);
//TODO: Change back to deepEqual once zipCodeCtu is added to saveSession.
}
// This function will verify schedule info is still valid.
// check to see if we have an appointment date on the order object.
// if so, make sure it's not in the past. if in the past, clear schedule info in store.
// if date not in past, then call schedule service to verify appointment is still available.
async function resetScheduleIfUnavailable(context, order, pageNameToLog) {
if (!order.schedule?.date) {
return;
}
// Date string with slashes is parsed as local time, not UTC. Our date has dashes, '-'.
// If you put any kind of time stamp on the date string with dashes, then it IS parsed as local time.
var aptDate = new Date(order.schedule.date + "T00:00:00");
var curDate = new Date();
curDate.setHours(0, 0, 0, 0);
// if appointment date is in the past, clear schedule
if (aptDate.getTime() < curDate.getTime()) {
context.commit(storeMutations.RESET_SCHEDULE);
return;
}
// create date range to pass to the schedule service to see if our appointment is still available.
var endRange = new Date(aptDate);
endRange.setDate(aptDate.getDate() + 1);
var endDay = "" + endRange.getDate();
var endMonth = "" + (endRange.getMonth() + 1); // 0 based so add 1
const endYear = endRange.getFullYear();
if (endMonth.length < 2) {
endMonth = "0" + endMonth;
}
if (endDay.length < 2) {
endDay = "0" + endDay;
}
const endDate = [endYear, endMonth, endDay].join("-");
let newTimeSlotsResponse;
if (order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE) {
newTimeSlotsResponse = await context.dispatch(
storeActions.GET_MOBILE_TIME_SLOTS,
{
payload: {
startDate: order.schedule.date,
endDate: endDate,
},
pageNameToLog: pageNameToLog,
},
false
);
if (newTimeSlotsResponse?.data.days?.length === 0) {
context.commit(storeMutations.RESET_SCHEDULE);
return;
}
var mobileRouteCodeFound = false;
// if early bird fee is in supporting items then need to check the timeslot to see if offer premium is also still available
if (
order.lineItem?.supportingItems?.findIndex(
(item) => item.partType == PREMIUM_FEE_PART_TYPE
)
) {
newTimeSlotsResponse.data.days?.forEach((day) => {
day.timeSlots.forEach((ts) => {
if (ts.id === order.schedule.routeCode && ts.offerPremium) {
mobileRouteCodeFound = true;
}
});
});
} else {
newTimeSlotsResponse.data.days?.forEach((day) => {
day.timeSlots.forEach((ts) => {
if (ts.id === order.schedule.routeCode) {
mobileRouteCodeFound = true;
}
});
});
}
if (!mobileRouteCodeFound) {
context.commit(storeMutations.RESET_SCHEDULE);
return;
}
} else {
newTimeSlotsResponse = await context.dispatch(
storeActions.GET_SHOP_TIME_SLOTS,
{
payload: {
startDate: order.schedule.date,
endDate: endDate,
shopAppointmentType: order.serviceLocation.appointmentType,
providerNumber: order.serviceLocation.provider.providerNumber,
},
pageNameToLog: pageNameToLog,
},
false
);
if (newTimeSlotsResponse?.data.days?.length === 0) {
context.commit(storeMutations.RESET_SCHEDULE);
return;
}
var routeCodeFound = false;
newTimeSlotsResponse.data.days?.forEach((day) => {
day.timeSlots.forEach((ts) => {
if (ts.id === order.schedule.routeCode) {
routeCodeFound = true;
}
});
});
if (!routeCodeFound) {
context.commit(storeMutations.RESET_SCHEDULE);
return;
}
}
}