diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index f99ea660d..aa0643821 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -192,6 +192,10 @@ const endpoints = { url: "/account/api/v1/account/bill-to-account-number", method: "GET", }, + ClosestApplicableShops: { + url: "/location/api/v1/location/closest-applicable-shops", + method: "GET", + }, AddDonationPart: { url: "/order/api/v1/order/add-donation-part", method: "POST", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 1e8945c49..7b76d4064 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -55,6 +55,7 @@ const storeActions = { GET_INSURANCE_COMPANY_LIST: "getInsuranceCompanyList", GET_BILL_TO_ACCOUNT_NUMBER: "getBillToAccountNumber", GET_RECAL_PARTS_AND_SAVE_TO_LINE_ITEMS: "getRecalPartsAndSaveToLineItems", + GET_CLOSEST_APPLICABLE_SHOPS: "getClosestApplicableShops", // Analytics LOG_EXPERIMENT_EXPOSURE_AND_UPDATE_STORE: "logExperimentExposureAndUpdateStore", diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 69e78138b..2f53d8133 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -9,6 +9,9 @@ const storeMutations = { UPDATE_MAKE: "updateMake", UPDATE_MODEL: "updateModel", UPDATE_STYLE: "updateStyle", + UPDATE_VEHICLE_SUBTYPE: "updateVehicleSubType", + UPDATE_VEHICLE_SPECIAL_CLASS: "updateVehicleSpecialClass", + UPDATE_IS_BIGTRUCK: "updateIsBigTruck", UPDATE_CAR_ID: "updateCarId", UPDATE_VEHICLE_CATEGORY: "updateVehicleCategory", UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", @@ -16,6 +19,7 @@ const storeMutations = { UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", UPDATE_VEHICLE_VIN: "updateVehicleVin", UPDATE_VEHICLE: "updateVehicle", + UPDATE_VEHICLE_CAN_SAFELITE_SERVICE: "updateVehicleCanSafeliteService", UPDATE_IS_REPAIR: "updateIsRepair", UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips", diff --git a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js index 7a9ac00d0..106ed892c 100644 --- a/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js +++ b/src/layouts/service-location/helpers/service-location-helper/service-location-helper.js @@ -149,6 +149,23 @@ export async function getAvailabilityRating( return shopStatus; } +export async function getClosestApplicableShops(serviceZipCode, carId, pageNameToLog) { + if (!serviceZipCode) { + return null; + } + + const closestShops = await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.GET_CLOSEST_APPLICABLE_SHOPS, + { + zip: serviceZipCode, + carId: carId, + }, + pageNameToLog + ); + + return closestShops.data; +} + export async function getZipCodeData(serviceZipCode) { return await baseMixin.methods.getZipCodeData(serviceZipCode, "service-location"); } diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index 56c76bb2d..a85a5bd17 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -15,6 +15,8 @@ editScreenReaderTextCmsWidgetName="ScreenReaderZipEditWidget" v-model="serviceZipCodeQuestion" ref="serviceZipCodeQuestion" + :carId="carId" + :isVehicleHeavyTruck="isVehicleHeavyTruck" :mobileFeePart="mobileFeePart" @updated-mobile-fee-part="setMobileFeePart" @updated-recycle-fee-part="setRecycleFeePart" @@ -186,9 +188,11 @@ export default { return { streetAddress: this.getServiceAddressFromStore(), apartmentNumberOrBusinessName: this.getServiceAddress2FromStore(), + carId: this.getCarIdfromStore(), city: this.getServiceCityFromStore(), state: this.getServiceStateFromStore(), zipCode: this.getServiceZipCodeFromStore(), + isVehicleHeavyTruck: this.getIsVehicleHeavyTruckFromStore(), isVehicleProtected: this.getIsVehicleProtectedFromStore(), isGlassServiceableInshop: null, isRecalibrationServiceableInshop: null, @@ -514,6 +518,9 @@ export default { }); } }, + getCarIdfromStore() { + return store.getters.vehicle.carId; + }, getServiceAddressFromStore() { return store.getters.order.serviceLocation.address; }, @@ -529,6 +536,9 @@ export default { getServiceZipCodeFromStore() { return store.getters.order.serviceLocation.zipCode; }, + getIsVehicleHeavyTruckFromStore() { + return store.getters.order.vehicle?.isBigTruck ?? false; + }, getIsVehicleProtectedFromStore() { return store.getters.order.serviceLocation.isVehicleProtected; }, diff --git a/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue index a2ab4134e..5e7aa73e4 100644 --- a/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue +++ b/src/layouts/service-location/service-zip-modal-question/service-zip-modal-question.vue @@ -33,6 +33,13 @@ cmsWidgetName="AlertInvalidZipWidget" alertClass="alert-danger" v-bind:isDismissible="false" /> + @@ -48,6 +55,7 @@ import { getPricedRecycleFeePart, getServiceabilityDetails, getBillToAccountNumber, + getClosestApplicableShops, } from "@/layouts/service-location/helpers/service-location-helper/service-location-helper"; export default { @@ -63,6 +71,7 @@ export default { internalModel: this.copyModel(this.modelValue), serviceZipCodeTextInputId: "", displayInvalidZipAlert: false, + displayNoServiceAlert: false, }; }, props: { @@ -73,6 +82,11 @@ export default { zipCode: "", }), }, + carId: String, + isVehicleHeavyTruck: { + type: Boolean, + default: false, + }, mobileFeePart: { type: Object, default: () => ({}), @@ -107,6 +121,7 @@ export default { methods: { resetAlerts() { this.displayInvalidZipAlert = false; + this.displayNoServiceAlert = false; }, resetsOnZipInput() { this.resetAlerts(); @@ -155,6 +170,22 @@ export default { this.focusOnZipInput(); this.resetModalButtonStyle(); } else { + if (this.isVehicleHeavyTruck) { + // check the location endpoint to verify this zip can service a heavy truck + const closestShops = await getClosestApplicableShops( + this.internalModel.zipCode, + this.carId, + "service-location" + ); + + if (!closestShops || closestShops.providers?.length === 0) { + this.displayNoServiceAlert = true; + this.focusOnZipInput(); + this.resetModalButtonStyle(); + return null; + } + } + this.internalModel.state = zipCodeData.state; this.internalModel.zipCodeCtu = zipCodeData.zipCodeCtu; diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 4a7818834..417a3b4c4 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -179,12 +179,12 @@ export default { } }); - this.pushEventToGA( - this.GaCategories.SERVICE_LOCATION, - gaAction, - shops.join(","), - true - ); + var joinedShops = shops.join(","); + if (!joinedShops) { + joinedShops = "no-shops"; + } + + this.pushEventToGA(this.GaCategories.SERVICE_LOCATION, gaAction, joinedShops, true); } if (this.answers.length === 0) { diff --git a/src/layouts/vehicle/vehicle.vue b/src/layouts/vehicle/vehicle.vue index 5f769b9b7..bad52fb02 100644 --- a/src/layouts/vehicle/vehicle.vue +++ b/src/layouts/vehicle/vehicle.vue @@ -48,6 +48,19 @@ validationRules="style-required" placeHolderText="Select style" customDropdownId="styleQuestionField" /> + +
+
+ +
+ @@ -557,4 +636,9 @@ export default { margin-top: 1.5rem; margin-bottom: 0; } +.separator-line { + grid-area: 2/1/2/8; + border-top: 1px solid $gray-500; + margin: 0.5rem 0; +} diff --git a/src/store/index.js b/src/store/index.js index 97a31068e..9fec43a77 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -66,9 +66,13 @@ const getDefaultState = () => { make: null, model: null, style: null, + vehicleSubType: null, + vehicleSpecialClass: null, + isBigTruck: false, carId: null, category: null, vin: null, + canSafeliteService: null, imageUrl: null, imageVifNumber: null, imageColor: null, @@ -216,12 +220,24 @@ export const mutations = { updateStyle(state, style) { state.order.vehicle.style = style; }, + updateVehicleSubType(state, vehicleSubType) { + state.order.vehicle.vehicleSubType = vehicleSubType; + }, + updateVehicleSpecialClass(state, vehicleSpecialClass) { + state.order.vehicle.vehicleSpecialClass = vehicleSpecialClass; + }, + updateIsBigTruck(state, isBigTruck) { + state.order.vehicle.isBigTruck = isBigTruck; + }, updateCarId(state, carId) { state.order.vehicle.carId = carId; }, updateVehicleCategory(state, category) { state.order.vehicle.category = category; }, + updateVehicleCanSafeliteService(state, canSafeliteService) { + state.order.vehicle.canSafeliteService = canSafeliteService; + }, updateVehicleImageUrl(state, imageUrl) { state.order.vehicle.imageUrl = imageUrl; }, @@ -1248,6 +1264,15 @@ export const actions = { }); }, + getClosestApplicableShops(context, { payload: { zip, carId }, pageNameToLog }) { + return globalMethods.callHttpClient({ + methods: endpoints.ClosestApplicableShops.method, + endpoint: `${endpoints.ClosestApplicableShops.url}/${zip}/${carId}`, + logApiCall: true, + pageNameToLog: pageNameToLog, + }); + }, + // Dependency Actions resetVehicleStateAndDependencies(context) { context.commit(storeMutations.RESET_VEHICLE_STATE); @@ -1835,6 +1860,8 @@ export const actions = { endPoint += `&${glassPieces}`; } + endPoint += `&isHeavyTruckVehicle=${context.getters.order.vehicle.isBigTruck}`; + return globalMethods.callHttpClient({ method: endpoints.GetServiceabilityDetails.method, endpoint: endPoint, @@ -1861,6 +1888,8 @@ export const actions = { if (windshieldPartWithRecal) { url += `/${windshieldPartWithRecal.partNumber}`; } + //heavy truck vehicle + url += `/${context.getters.order.vehicle.isBigTruck}`; return globalMethods.callHttpClient({ method: endpoints.GetProviders.method, @@ -2135,6 +2164,9 @@ export const actions = { year: vehicle.year, make: vehicle.make, model: vehicle.model, + subType: vehicle.vehicleSubType, + specialClass: vehicle.vehicleSpecialClass, + isBigTruck: vehicle.isBigTruck, isInsurance: order.payment.isInsurance ?? false, isVerified: order.payment.insuranceCoverage.isVerified ?? false, lineItems: { @@ -2188,6 +2220,9 @@ export const actions = { model: vehicle.model, style: vehicle.style, vin: vehicle.vin, + vehicleSubType: vehicle.vehicleSubType, + vehicleSpecialClass: vehicle.vehicleSpecialClass, + isBigTruck: vehicle.isBigTruck, registration: { firstName: vehicle.registration.firstName, lastName: vehicle.registration.lastName, @@ -2398,7 +2433,21 @@ export const actions = { // Vehicle saveVehicle( context, - { year, make, model, style, carId, category, imageUrl, imageVifNumber, imageVifColor } + { + year, + make, + model, + style, + vehicleSubType, + vehicleSpecialClass, + isBigTruck, + carId, + category, + canSafeliteService, + imageUrl, + imageVifNumber, + imageVifColor, + } ) { if ( context.state.order.vehicle.year != year || @@ -2414,8 +2463,12 @@ export const actions = { context.commit(storeMutations.UPDATE_MAKE, make); context.commit(storeMutations.UPDATE_MODEL, model); context.commit(storeMutations.UPDATE_STYLE, style); + context.commit(storeMutations.UPDATE_VEHICLE_SUBTYPE, vehicleSubType); + context.commit(storeMutations.UPDATE_VEHICLE_SPECIAL_CLASS, vehicleSpecialClass); + context.commit(storeMutations.UPDATE_IS_BIGTRUCK, isBigTruck); context.commit(storeMutations.UPDATE_CAR_ID, carId); context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, category); + context.commit(storeMutations.UPDATE_VEHICLE_CAN_SAFELITE_SERVICE, canSafeliteService); 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);