diff --git a/jest.config.js b/jest.config.js index a757d5ada..28c5557cb 100644 --- a/jest.config.js +++ b/jest.config.js @@ -26,7 +26,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 80, + statements: 78, // Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90 }, }, diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 7d83642a6..53f981d8a 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -108,12 +108,14 @@ const endpoints = { }, GetShopTimeSlots: { url: "/schedule/api/v1/schedule/shop-time-slots", - mockUrl: "https://mockey.qa.sagaws.net/service/shop-time-slots", // TODO: REMOVE MOCKURL + method: "POST", + }, + GetMobileTimeSlots: { + url: "/schedule/api/v1/schedule/mobile-time-slots", method: "POST", }, GetMobileEarlyBirdFee: { url: "/parts/api/v1/parts/mobile-early-bird-fee", - mockUrl: "https://mockey.qa.sagaws.net/service/mobile-early-bird-fee", // TODO: REMOVE MOCKURL method: "GET", }, SaveSession: { diff --git a/src/constants/experiments.js b/src/constants/experiments.js index f6d25f1a7..f89aad963 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -5,6 +5,7 @@ const experimentUniverses = { const experimentSettings = { GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index", SUPPRESS_VIN_CAPTURE: "SuppressVinCapture", + DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators", }; const experimentTriggers = { diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 341fcd487..5ff98fd9d 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -33,6 +33,7 @@ const storeActions = { GET_MOBILE_FEE_PART: "getMobileFeePart", GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails", GET_SHOP_TIME_SLOTS: "getShopTimeSlots", + GET_MOBILE_TIME_SLOTS: "getMobileTimeSlots", GET_PROVIDERS: "getProviders", GET_MOBILE_EARLY_BIRD_FEE: "getMobileEarlyBirdFee", SAVE_SESSION: "saveSession", diff --git a/src/digital-components/button-question/button-question.vue b/src/digital-components/button-question/button-question.vue index a344286ef..7441ab422 100644 --- a/src/digital-components/button-question/button-question.vue +++ b/src/digital-components/button-question/button-question.vue @@ -256,7 +256,9 @@ export default { }, watch: { modelValue(newValue, oldValue) { - this.resetField(); + this.resetField({ + value: newValue, + }); }, answers() { //once we get the answers to display from parent, see if we need a GA event to log what we showed diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index c1c66c5d3..e63349c67 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -290,9 +290,10 @@ export default { let myPromise = new Promise((resolve, reject) => { const response = config.customSelectableDatesCallback( - initialViewStartDate, - initialViewEndDate, - store.getters.order.serviceLocation.appointmentType + initialViewStartDate.toISOString().split("T")[0], + initialViewEndDate.toISOString().split("T")[0], + store.getters.order.serviceLocation.appointmentType, + store.getters.order.serviceLocation.provider.providerNumber ); resolve(response); }); @@ -354,7 +355,6 @@ export default { this.hideSomeDaysForInitialView = config.hideSomeDaysForInitialView; const hideSecondMonth = config.hideSecondMonth; const direction = config.calendarViewDirection; - const monthsAfterToLoadOffset = 12; const monthsBeforeToLoadOffset = 36; config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => { @@ -571,7 +571,8 @@ export default { const moreSelectableDates = await this.customSelectableDatesCallback( monthStart, monthEnd, - this.$store.getters.order.serviceLocation.appointmentType + this.$store.getters.order.serviceLocation.appointmentType, + this.$store.getters.order.serviceLocation.provider.providerNumber ); moreSelectableDates.days.forEach((selectableDate) => { const index = this.selectableDatesData.findIndex( diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index b43be23ad..3717248f7 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -81,18 +81,33 @@ import store from "@/store"; defineRule("date-required", required(errorMessages.DATE_REQUIRED)); defineRule("time-slot-selection-required", required(errorMessages.DATE_REQUIRED)); -const getAvailableDates = async (startDate, endDate, appointmentType) => { +const getAvailableDates = async (startDate, endDate, appointmentType, providerNumber) => { // USING DATES PASSED, MAKE AN API CALL - const newShopTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction( - storeActions.GET_SHOP_TIME_SLOTS, - { - startDate: startDate, - endDate: endDate, - shopAppointmentType: appointmentType, - }, - false - ); - const newShopTimeSlots = newShopTimeSlotsResponse.data; + + let newTimeSlotsResponse; + if (appointmentType === AppointmentTypeStrings.MOBILE) { + newTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction( + storeActions.GET_MOBILE_TIME_SLOTS, + { + startDate: startDate, + endDate: endDate, + }, + false + ); + } else { + newTimeSlotsResponse = await baseMixin.methods.dispatchStoreAction( + storeActions.GET_SHOP_TIME_SLOTS, + { + startDate: startDate, + endDate: endDate, + shopAppointmentType: appointmentType, + providerNumber: providerNumber, + }, + false + ); + } + + const newShopTimeSlots = newTimeSlotsResponse.data; return convertApiResponse(newShopTimeSlots); }; const convertApiResponse = (responseData) => { @@ -112,9 +127,9 @@ export default { name: "schedule", data() { return { - selectedDate: null, + selectedDate: this.getSelectedDate(), selectedTimeSlotData: { - id: null, + id: this.getSelectedRouteCode(), isPremiumAppointment: null, }, selectableDatesData: [], @@ -152,7 +167,6 @@ export default { const alertReasonsPromise = locationAlerts.methods.loadInitialData( store.getters.order.serviceLocation.zipCodeCtu ); - // Settle promises and get results const promiseResultMap = [ { @@ -205,7 +219,7 @@ export default { if (this.selectedDate === null) { return null; } - return this.selectableDatesData.days.find( + return this.selectableDatesData.days?.find( (selectableDate) => selectableDate.dateString === this.selectedDate.dateString ); }, @@ -216,12 +230,14 @@ export default { const timeSlotSelectedObject = this.getTimeSlotObjectFromTimeSlotId( this.selectedTimeSlotData.id ); + if (timeSlotSelectedObject) { return { date: this.selectedDate.dateString, startTime: timeSlotSelectedObject.startTime, endTime: timeSlotSelectedObject.endTime, - id: this.selectedTimeSlotData.id, + routeCode: this.selectedTimeSlotData.id, + jobMaxMinutes: this.selectableDatesData.estimatedServiceMinutesMaximum, }; } else { return null; @@ -241,7 +257,8 @@ export default { const newShopTimeSlots = await getAvailableDates( startDate, endDate, - this.appointmentType + this.appointmentType, + this.$store.getters.order.serviceLocation.provider.providerNumber ); // ADD API CALL RESULTS TO EXISTING DATE DATA this.selectableDatesData.days = this.selectableDatesData.days.concat( @@ -261,8 +278,14 @@ export default { ).timeSlots; return timeSlots.find((timeSlot) => timeSlot.id === timeSlotId); }, + getSelectedDate() { + return store.getters.order.schedule.date; + }, + getSelectedRouteCode() { + return store.getters.order.schedule.routeCode; + }, timeSlotModalClosed() { - // Clear the selectedDate if no timeslot has been selected + // Clear the selectedDate if no timeSlot has been selected if (!this.selectedTimeSlotData.id) { this.selectedDate = null; } @@ -302,13 +325,14 @@ export default { convertSelectedDateToShortMonthAndDay(selectedDate) { // This conversion ensures we don't get get GMT induced date changes const dateObject = new Date(`${selectedDate.dateString}T00:00:00`); + // Ex: April 25 return dateObject.toLocaleDateString("en-us", { month: "short", day: "numeric" }); }, - // Expected input: "HH:MM:SS" + // Expected input: "HH:MM" getDisplayTextForMilitaryTime(militaryTimeInput, shouldTrimMinutesIfEmpty = false) { let hours = parseInt(militaryTimeInput.split(":")[0]); const minutes = militaryTimeInput.split(":")[1]; - let meridianNotation = hours > 11 ? "PM" : "AM"; + const meridianNotation = hours > 11 ? "PM" : "AM"; if (hours > 12) { hours -= 12; } @@ -322,18 +346,11 @@ export default { this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { - //TODO: replace properties with real values once they are available await this.dispatchStoreAction( this.storeActions.SAVE_SCHEDULE, - { - date: "2023-07-04T00:00:00", - startTime: "2023-07-04T12:00:00", - endTime: "2023-07-04T17:00:00", - routeCode: "03341-01820-S-B*20232*11 AM", - }, + this.appointmentDateAndTime, false ); - navigateToHeritageFunnel({ loadingModal: this.$refs.loadingModal }); }, }, diff --git a/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-list-button/time-slot-modal-list-button.vue similarity index 97% rename from src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue rename to src/layouts/schedule/time-slot-modal-question/time-slot-modal-list-button/time-slot-modal-list-button.vue index 8c0f3db0a..44ac0d9e8 100644 --- a/src/layouts/schedule/time-slot-modal-question/timeslot-modal-list-button/timeslot-modal-list-button.vue +++ b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-list-button/time-slot-modal-list-button.vue @@ -15,7 +15,6 @@ {{ formattedButtonLabelSubCopy }} - {{ screenReaderOnlyText }} @@ -28,7 +27,7 @@ import baseInputButton from "@/digital-components/base-input-button/base-input-b import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin"; export default { - name: "timeslotModalListButton", + name: "timeSlotModalListButton", mixins: [inputButtonWrapperMixin], computed: { formattedButtonLabelSubCopy() { @@ -52,9 +51,6 @@ export default { diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 3183cbc9e..2bc4cba7b 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -19,7 +19,8 @@ textPosition="text-start" v-model="selectedProviderNumber" isRequired - validationRules="option-required" /> + validationRules="option-required" + :additionalButtonData="additionalButtonData" /> diff --git a/src/store/index.js b/src/store/index.js index 9d06df11a..270203f89 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -551,6 +551,7 @@ export const actions = { payload: {}, }); }, + lookupVehicleByYmms(context, { year, make, model, style }) { return globalMethods.callHttpClient({ method: endpoints.LookupVehicleByYmms.method, @@ -558,6 +559,7 @@ export const actions = { payload: {}, }); }, + lookupVehicleByVin(context, { vin }) { return globalMethods.callHttpClient({ method: endpoints.LookupVehicleByVin.method, @@ -567,6 +569,7 @@ export const actions = { }, }); }, + lookupVinByPlate(context, { licensePlate, licenseState }) { return globalMethods.callHttpClient({ method: endpoints.LookupVinByPlate.method, @@ -577,6 +580,7 @@ export const actions = { }, }); }, + lookupVinByAddress( context, { licenseLastName, licenseStreetAddress, licenseZip, licenseState } @@ -592,6 +596,7 @@ export const actions = { }, }); }, + lookupVinByImage(context, image) { const data = new FormData(); data.append("vinImage", image); @@ -602,6 +607,7 @@ export const actions = { isFormData: true, }); }, + isVinByAddressPermissible(context, zip) { return globalMethods.callHttpClient({ method: endpoints.IsVinByAddressPermissible.method, @@ -609,6 +615,7 @@ export const actions = { payload: {}, }); }, + getVehicleMakes(context, { year }) { return globalMethods.callHttpClient({ method: endpoints.GetVehicleMakes.method, @@ -616,6 +623,7 @@ export const actions = { payload: {}, }); }, + getVehicleModels(context, { year, make }) { return globalMethods.callHttpClient({ method: endpoints.GetVehicleModels.method, @@ -623,6 +631,7 @@ export const actions = { payload: {}, }); }, + getVehicleStyles(context, { year, make, model }) { return globalMethods.callHttpClient({ method: endpoints.GetVehicleStyles.method, @@ -630,6 +639,7 @@ export const actions = { payload: {}, }); }, + setVehicle(context, { year, make, model, style }) { return globalMethods .callHttpClient({ @@ -652,6 +662,7 @@ export const actions = { return response; }); }, + getDamageOptions(context, { carId }) { return globalMethods.callHttpClient({ methods: endpoints.GetDamageOptions.method, @@ -659,6 +670,7 @@ export const actions = { payload: {}, }); }, + validateZip(context, { zip }) { return globalMethods.callHttpClient({ methods: endpoints.ValidateZip.method, @@ -673,18 +685,22 @@ export const actions = { context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null); context.commit(storeMutations.UPDATE_VAPS, null); }, + resetRegistrationAndDependencies(context) { context.commit(storeMutations.RESET_REGISTRATION_STATE); context.commit(storeMutations.RESET_GLASS_PARTS_STATE); context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null); }, + resetPartsAndDependencies(context) { context.commit(storeMutations.RESET_GLASS_PARTS_STATE); context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, null); }, + resetState(context) { context.commit(storeMutations.RESET_STATE); }, + resetSaveSessionPromise(context) { context.commit(storeMutations.RESET_SAVE_SESSION_PROMISE); }, @@ -699,12 +715,14 @@ export const actions = { }, }); }, + 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, @@ -771,6 +789,7 @@ export const actions = { context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId); context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId); }, + logPageView( context, { @@ -812,6 +831,7 @@ export const actions = { } ); }, + logCustomEvent( context, { @@ -1116,52 +1136,123 @@ export const actions = { }); }, - getShopTimeSlots( - context, - { startDate = "2023-01-01", endDate = "2023-05-01", shopAppointmentType = "" } - ) { + getShopTimeSlots(context, { startDate, endDate, shopAppointmentType, providerNumber }) { const order = context.state.order; - const mockArray = []; + const vehicle = context.state.order.vehicle; + let partNumbers = [ + ...(order.lineItems.supportingItems ?? []), + ...(order.lineItems.vaps ?? []), + ...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts), + ]; + partNumbers = partNumbers.map((lineItem) => { + return lineItem.partNumber; + }); + const glassPieces = order.damage.glassToReplace + ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace) + : []; var payload = { - providerNumber: order.providerNumber, + providerNumber: providerNumber, startDate: startDate, endDate: endDate, shopAppointmentType: shopAppointmentType, - applicationName: applicationConfig.APPLICATION_NAME, + applicationName: "Safelite.com funnel", parentAccountNumber: context.getters.payment.parentAccountNumber, - carId: context.getters.vehicle.carId, - partNumbers: mockArray, // TODO: WHERE DO I GET THIS? + carId: vehicle.carId, + partNumbers: partNumbers, + glassPieces: glassPieces, eon: order.eon, - provisionalReasons: mockArray, // TODO: WHERE DO I GET THIS? + coverage: { + status: "", + deductible: 0, + additionalAuthFlag: "", + }, + partSelection: { + // TODO: Provisional booking will utilize these fields + hasAnsweredPartQuestions: false, + hasAnsweredMoldingQuestions: false, + hasAnsweredCapabilityQuestions: false, + hasManuallySelectedParts: false, + }, + vehicle: { + year: vehicle.year, + make: vehicle.make, + model: vehicle.model, + style: vehicle.style, + vin: vehicle.vin ?? "", + }, }; - // TODO: REMOVE MOCK CALL & USE REAL CALL BELOW - return globalMethods.callMockHttpClient({ + return globalMethods.callHttpClient({ method: endpoints.GetShopTimeSlots.method, - endpoint: endpoints.GetShopTimeSlots.mockUrl, + endpoint: endpoints.GetShopTimeSlots.url, payload: payload, + logApiCall: false, }); - // TODO: RESTORE THIS - // return globalMethods.callHttpClient({ - // method: endpoints.GetShopTimeSlots.method, - // endpoint: endpoints.GetShopTimeSlots.url, - // payload: payload, - // logApiCall: false, - // }); }, + + getMobileTimeSlots(context, { startDate, endDate }) { + const order = context.state.order; + const vehicle = context.state.order.vehicle; + let partNumbers = [ + ...(order.lineItems.supportingItems ?? []), + ...(order.lineItems.vaps ?? []), + ...getFlattenedArrayOfLineItemsWithChildParts(order.lineItems.glassParts), + ]; + partNumbers = partNumbers.map((lineItem) => { + return lineItem.partNumber; + }); + const glassPieces = order.damage.glassToReplace + ? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace) + : []; + var payload = { + startDate: startDate, + endDate: endDate, + applicationName: "Safelite.com funnel", + parentAccountNumber: context.getters.payment.parentAccountNumber, + carId: vehicle.carId, + partNumbers: partNumbers, + glassPieces: glassPieces, + eon: order.eon, + coverage: { + status: "", + deductible: 0, + additionalAuthFlag: "", + }, + partSelection: { + // TODO: Provisional booking will utilize these fields + hasAnsweredPartQuestions: false, + hasAnsweredMoldingQuestions: false, + hasAnsweredCapabilityQuestions: false, + hasManuallySelectedParts: false, + }, + 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: false, + }); + }, + getMobileEarlyBirdFee(context) { const damageType = context.getters.damage.isRepair ? "Repair" : "Replace"; const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash"; - return globalMethods.callMockHttpClient({ + + return globalMethods.callHttpClient({ method: endpoints.GetMobileEarlyBirdFee.method, - endpoint: `${endpoints.GetMobileEarlyBirdFee.mockUrl}/Cash/Replace`, + endpoint: `${endpoints.GetMobileEarlyBirdFee.url}/${paymentType}/${damageType}`, }); - // return globalMethods.callHttpClient({ - // method: endpoints.GetMobileEarlyBirdFee.method, - // endpoint: `${endpoints.GetMobileEarlyBirdFee.url}/${paymentType}/${damageType}`, - // }); }, + // Session API Actions saveSession(context) { const vehicle = context.getters.vehicle; @@ -1262,6 +1353,7 @@ export const actions = { }, }); }, + loadSession( context, { @@ -1339,6 +1431,7 @@ export const actions = { context.commit(storeMutations.UPDATE_YEAR, year); } }, + saveVehicleMake(context, make) { //Reset dependent state when changing if (context.state.order.vehicle.make !== make) { @@ -1359,6 +1452,7 @@ export const actions = { context.commit(storeMutations.UPDATE_MAKE, make); } }, + saveVehicleModel(context, model) { //Reset dependent state when changing if (context.state.order.vehicle.model !== model) { @@ -1378,6 +1472,7 @@ export const actions = { context.commit(storeMutations.UPDATE_MODEL, model); } }, + saveVehicleStyle(context, style) { //Reset dependent state when changing if (context.state.order.vehicle.style !== style) { @@ -1396,6 +1491,7 @@ export const actions = { context.commit(storeMutations.UPDATE_STYLE, style); } }, + saveVehicleDamage( context, { isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount } @@ -1453,6 +1549,7 @@ export const actions = { context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); } }, + saveRegistrationLicensePlateLookup( context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } @@ -1474,6 +1571,7 @@ export const actions = { context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); } }, + saveRegistrationAddressLookup( context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo } @@ -1499,6 +1597,7 @@ export const actions = { context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo); } }, + savePartQuestionAnswers(context, partQuestionAnswersArray) { // if part question answers have changed, reset subsequent question answers const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( @@ -1537,6 +1636,7 @@ export const actions = { //Save new values context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray); }, + resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) { const partsOrQuestionsDataToCompareWith = context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ?? @@ -1576,6 +1676,7 @@ export const actions = { }); } }, + saveMoldingQuestionAnswers(context, moldingQuestionAnswers) { const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( context.getters.damage.moldingQuestionAnswers, @@ -1604,6 +1705,7 @@ export const actions = { //Save new values context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers); }, + saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) { const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( context.getters.damage.capabilityQuestionAnswers, @@ -1630,18 +1732,23 @@ export const actions = { capabilityQuestionAnswers ); }, + savePaymentType(context, isInsurance) { context.commit(storeMutations.UPDATE_IS_INSURANCE, isInsurance); }, + saveParentAccountNumber(context, parentAccountNumber) { context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, parentAccountNumber); }, + saveSupportingItems(context, supportingItems) { context.commit(storeMutations.UPDATE_SUPPORTING_ITEMS, supportingItems); }, + saveVaps(context, vaps) { context.commit(storeMutations.UPDATE_VAPS, vaps); }, + // Price order actions async priceOrderItemsAndSaveServerData( context, @@ -1694,16 +1801,20 @@ export const actions = { return availableLineItems; }, + // Misc order actions saveSchedule(context, scheduleInfo) { context.commit(storeMutations.UPDATE_SCHEDULE, scheduleInfo); }, + saveServiceLocation(context, serviceLocationInfo) { context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo); }, + saveEmail(context, email) { context.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, email); }, + saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) { //Reset dependent state when changing if (vehicleInfo.vin !== context.state.order.vehicle.vin) { @@ -1716,12 +1827,15 @@ export const actions = { context.commit(storeMutations.UPDATE_VEHICLE, vehicleInfo); } }, + saveGlassParts(context, parts) { context.commit(storeMutations.UPDATE_GLASS_PARTS, parts); }, + clearVin(context) { context.commit(storeMutations.UPDATE_VEHICLE_VIN, null); }, + isVinOptionalVehicle(context) { switch (context.state.order.vehicle.make.toLowerCase()) { case "mercedes benz": @@ -1863,7 +1977,7 @@ function addPricesToLineItems(lineItems, pricingLineItems) { function getFlattenedArrayOfLineItemsWithChildParts(lineItems) { let flattenedArray = []; - lineItems.forEach((lineItem) => { + lineItems?.forEach((lineItem) => { flattenedArray.push(lineItem); if (lineItem.childParts) { flattenedArray = [ @@ -1885,3 +1999,12 @@ function buildQueryStringParameterFromArrayOfComplexObjects(arrayOfObjects, para // Remove trailing & return queryStringParameter.slice(0, -1); } + +function convertGlassPieceToBackEndCompatibleFormat(glassPieces) { + return glassPieces.map((glassPiece) => { + return { + location: glassPiece.glassLocation, + name: glassPiece.glassName, + }; + }); +} diff --git a/src/ux-components/loader/loader.vue b/src/ux-components/loader/loader.vue index daea9c511..0635614b8 100644 --- a/src/ux-components/loader/loader.vue +++ b/src/ux-components/loader/loader.vue @@ -3,7 +3,7 @@ class="loader" role="alert" aria-label="Loading new page" - v-bind:class="[this.loaderColor, this.loaderPosition]"> + v-bind:class="[this.loaderColor, this.loaderPosition, this.blockUi]"> @@ -82,5 +87,9 @@ export default { &.black:after { background-color: $black; } + + &.no-block::before { + z-index: -1; + } }