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, }, 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; }, 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; 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, } ) { 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); }, 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); console.log(response.data); 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 lineItemsWithOnlyPriceInfo = pricedAvailableLineItems.map((lineItem) => ({ partNumber: lineItem.partNumber, laborAmount: lineItem.laborAmount ?? 0, kitPrice: lineItem.kitPrice ?? 0, sellingPrice: lineItem.sellingPrice ?? 0, })); const pricedLineItemsFormattedForRequest = buildQueryStringParameterFromArrayOfComplexObjects( lineItemsWithOnlyPriceInfo, "lineItems" ); let queryString = `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + `&BillToAccountNumber=${billToAccountNumber}` + `&ProviderNumber=${providerNumber}` + `&AppointmentType=${appointmentType}` + `&ServiceLocation.City=${serviceLocationCity}` + `&ServiceLocation.State=${serviceLocationState}` + `&ServiceLocation.ZipCode=${serviceLocationZipCode}` + `&${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); }, saveGlassPartPrices(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; } } }