diff --git a/src/constants/schedule-constants.js b/src/constants/schedule-constants.js index 35232c04..af0c6206 100644 --- a/src/constants/schedule-constants.js +++ b/src/constants/schedule-constants.js @@ -1,7 +1,7 @@ const AppointmentTypeStrings = { IN_SHOP: 'Inshop', MOBILE: 'Mobile', - MOBILE_INSURANCE: 'Mobile-Insurance', + MOBILE_NOT_ITAC: 'Mobile-Not-ITAC', DROP_OFF: 'Dropoff' }; const PREMIUM_FEE_PART_TYPE = 'EARLY BIRD'; diff --git a/src/iss-components/site-header/menu-modal/menu-modal.vue b/src/iss-components/site-header/menu-modal/menu-modal.vue index 3c248abf..ddeee120 100644 --- a/src/iss-components/site-header/menu-modal/menu-modal.vue +++ b/src/iss-components/site-header/menu-modal/menu-modal.vue @@ -162,7 +162,7 @@ export default { border-top: 1px solid $gray-300; overflow-x: visible; overflow-y: visible; - z-index: 2; + z-index: 3; .modal-body { padding: 2rem; .ccpa-icon { diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index 624acf75..76db7eff 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -29,7 +29,25 @@ class="text-link-small" :customSelectableDatesCallback="getAvailableDatesMethod" validationRules="date-required" - @date-clicked="openInshopTimeSlotsModal" /> + @dateClicked="openInshopTimeSlotsModal" /> + 0); + return serviceLocationPreReqs && paymentInfo && supportingItems && damageInfo; }, setData(initialShopTimeSlotsResponse) { @@ -406,22 +434,14 @@ export default { supportingItems.push(this.mobilePremiumAppointmentFee); } - this.dispatchStoreAction( - this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, - supportingItems, - false - ); + this.mainStore.saveSupportingItemsSuppressingStateResetting(supportingItems); } else { // if it's not a mobile and/or premium early bird, then make sure we remove any that may have been added const removeEarlyBirdIndex = supportingItems.findIndex((item) => item.partType === PREMIUM_FEE_PART_TYPE); if (removeEarlyBirdIndex >= 0) { supportingItems.splice(removeEarlyBirdIndex, 1); - this.dispatchStoreAction( - this.storeActions.SAVE_SUPPORTING_ITEMS_SUPPRESSING_STATE_RESETTING, - supportingItems, - false - ); + this.mainStore.saveSupportingItemsSuppressingStateResetting(supportingItems); } } }, @@ -429,7 +449,9 @@ export default { this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); }, forwardButtonAction() { - // validate and save here + this.updateSupportingItems(); + this.mainStore.saveSchedule(this.selectedTimeSlotInfo.timeSlot); + this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD, this.$route); } } diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue index c8eb4d56..dc7dadb2 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue @@ -52,14 +52,14 @@ export default { }, answersToDisplay() { const shouldShowMobile = this.isServiceableMobile && this.isITAC; - const shouldShowMobileInsurance = this.isServiceableMobile && !this.isITAC; + const shouldShowMobileNotITAC = this.isServiceableMobile && !this.isITAC; const shouldShowInshop = this.isServiceableInshop; const shouldShowDropoff = this.isServiceableInshop && !useMainStore().damage.isRepair; return this.answersFromCms ? this.answersFromCms.filter((answer) => ( (answer.Name === AppointmentTypeStrings.IN_SHOP && shouldShowInshop) || (answer.Name === AppointmentTypeStrings.MOBILE && shouldShowMobile) - || (answer.Name === AppointmentTypeStrings.MOBILE_INSURANCE && shouldShowMobileInsurance) + || (answer.Name === AppointmentTypeStrings.MOBILE_NOT_ITAC && shouldShowMobileNotITAC) || (answer.Name === AppointmentTypeStrings.DROP_OFF && shouldShowDropoff) )) : []; @@ -86,12 +86,12 @@ export default { if ( newValue.length === 1 && newValue.findIndex((answer) => (answer.Name === AppointmentTypeStrings.MOBILE - || answer.Name === AppointmentTypeStrings.MOBILE_INSURANCE)) !== -1 + || answer.Name === AppointmentTypeStrings.MOBILE_NOT_ITAC)) !== -1 ) { if (this.isITAC) { this.selectedValues = AppointmentTypeStrings.MOBILE; } else { - this.selectedValues = AppointmentTypeStrings.MOBILE_INSURANCE; + this.selectedValues = AppointmentTypeStrings.MOBILE_NOT_ITAC; } } }, @@ -103,7 +103,7 @@ export default { if (this.isITAC) { this.selectedValues = AppointmentTypeStrings.MOBILE; } else { - this.selectedValues = AppointmentTypeStrings.MOBILE_INSURANCE; + this.selectedValues = AppointmentTypeStrings.MOBILE_NOT_ITAC; } } } diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index d0b8af66..62484df8 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -262,7 +262,7 @@ export default { if (newValue.zipCode !== this.zipCode) { if (!(this.selectedAppointmentType === AppointmentTypeStrings.MOBILE - || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_INSURANCE)) { + || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC)) { this.selectedAppointmentType = null; } this.selectedProvider = null; @@ -293,7 +293,7 @@ export default { }, isMobileLocationDisplayed() { return this.selectedAppointmentType === AppointmentTypeStrings.MOBILE - || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_INSURANCE; + || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC; }, requiresInshopRecalibration() { // Specifically check for isRecalibrationServiceableMobile === false, not null or true. diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 220f4ad4..59ded30c 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -142,7 +142,7 @@ export default { await nextTick(); - if (newValue !== AppointmentTypeStrings.MOBILE && newValue !== AppointmentTypeStrings.MOBILE_INSURANCE) { + if (newValue !== AppointmentTypeStrings.MOBILE && newValue !== AppointmentTypeStrings.MOBILE_NOT_ITAC) { this.getNextShopsFromList(); } } diff --git a/src/store/index.js b/src/store/index.js index 1e8e95de..6c40ab5b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -11,7 +11,7 @@ import applicationConfig from '@/constants/application-config'; import issPageValues from '@/router/router-constants/issPage-values'; import damageLocationsSelected from '@/constants/damage-locations-selected'; import coverageStatuses from '@/constants/coverage-statuses'; -import { PREMIUM_FEE_PART_TYPE } from '@/constants/schedule-constants'; +import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from '@/constants/schedule-constants'; import getDateDifferenceInDays from '@/helpers/date-helper'; const storeId = 'main'; @@ -121,7 +121,8 @@ const getDefaultState = () => ({ isInsurance: true, // TODO delete; irrelevant to ISS insuranceCoverage: { isVerified: false, - coverageStatus: coverageStatuses.PENDING + coverageStatus: coverageStatuses.PENDING, + claimNumber: null }, parentAccountNumber: 0 }, @@ -138,7 +139,8 @@ const getDefaultState = () => ({ startTime: null, endTime: null, routeCode: null, - jobMaxMinutes: null + jobMaxMinutes: null, + jobMinMinutes: null }, referralNumber: null, referralDate: null, @@ -192,6 +194,9 @@ export const useMainStore = defineStore({ payment: (state) => state.order.payment, policy: (state) => state.order.policy, hasAnyNonWindshieldGlassParts: (state) => !state.order.policy.isDamageGlassOnly, + isMobileAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE + || state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC, + isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF, isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, eventBusItem: (state) => (eventCategory, eventSubCategory) => { const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory); @@ -469,17 +474,20 @@ export const useMainStore = defineStore({ }).then((response) => { const registerClaimFailed = response.data.isError; order.payment.insuranceCoverage.isVerified = !registerClaimFailed; + order.payment.insuranceCoverage.claimNumber = null; if (registerClaimFailed) { this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; } else if (this.policy.noCoverage) { this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.NO_COMP; } else { this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.VERIFIED; + this.order.payment.insuranceCoverage.claimNumber = response.data.claimNumber; } return resolve(response); }, (error) => { this.order.payment.insuranceCoverage.isVerified = false; this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; + this.order.payment.insuranceCoverage.claimNumber = null; return reject(error); }); }); @@ -633,7 +641,7 @@ export const useMainStore = defineStore({ startDate, endDate, applicationName: applicationConfig.APPLICATION_NAME, - parentAccountNumber: this.payment.parentAccountNumber, + parentAccountNumber: this.issConfig.parentAccountNumber, // this.payment.parentAccountNumber, carId: vehicle.carId, lineItems, glassPieces, @@ -695,7 +703,7 @@ export const useMainStore = defineStore({ endDate, shopAppointmentType, applicationName: applicationConfig.APPLICATION_NAME, - parentAccountNumber: 167132, // this.payment.parentAccountNumber, + parentAccountNumber: this.issConfig.parentAccountNumber, // this.payment.parentAccountNumber, carId: vehicle.carId, lineItems, glassPieces, @@ -933,7 +941,8 @@ export const useMainStore = defineStore({ policyFirstName: customer.firstName, policyLastName: customer.lastName, policyPhoneNumber: customer.phoneNumber, - policyEmail: customer.emailAddress + policyEmail: customer.emailAddress, + policyState: customer.address.state }, policyNumber: policy.policyNumber, policyZipCode: policy.policyZipCode, @@ -964,7 +973,8 @@ export const useMainStore = defineStore({ payment: { InsuranceCoverage: { isVerified: payment.insuranceCoverage?.isVerified ?? false, - coverageStatus: payment.insuranceCoverage?.coverageStatus + coverageStatus: payment.insuranceCoverage?.coverageStatus, + claimNumber: payment.insuranceCoverage?.claimNumber }, isInsurance: payment.isInsurance ?? true, parentAccountNumber: this.issConfig.parentAccountNumber @@ -996,7 +1006,8 @@ export const useMainStore = defineStore({ startTime: schedule.startTime, endTime: schedule.endTime, routeCode: schedule.routeCode, - jobMaxMinutes: schedule.jobMaxMinutes + jobMaxMinutes: schedule.jobMaxMinutes, + jobMinMinutes: schedule.jobMinMinutes }, referralDate: this.order.referralDate, referralNumber: this.order.referralNumber?.toString(), @@ -1190,7 +1201,7 @@ export const useMainStore = defineStore({ this.order.schedule.endTime = null; this.order.schedule.routeCode = null; this.order.schedule.jobMaxMinutes = null; - // this.order.schedule.jobMinMinutes = null; + this.order.schedule.jobMinMinutes = null; // premium appointment fee used on schedule page also needs reset when schedule is reset const { supportingItems } = this.order.lineItems; @@ -1395,6 +1406,10 @@ export const useMainStore = defineStore({ this.order.lineItems.glassParts = glassParts; }, saveSupportingItems(supportingItems) { + // TODO: RESET_SERVICE_LOCATION_STATE_AND_DEPENDENCIES + this.order.lineItems.supportingItems = supportingItems; + }, + saveSupportingItemsSuppressingStateResetting(supportingItems) { this.order.lineItems.supportingItems = supportingItems; }, saveVaps(vaps) { @@ -1474,6 +1489,21 @@ export const useMainStore = defineStore({ }); }, + // Schedule Actions + saveSchedule(scheduleInfo) { + this.updateSchedule(scheduleInfo); + }, + updateSchedule(scheduleInfo) { + if (scheduleInfo) { + this.order.schedule.date = scheduleInfo.date; + this.order.schedule.startTime = scheduleInfo.startTime; + this.order.schedule.endTime = scheduleInfo.endTime; + this.order.schedule.routeCode = scheduleInfo.routeCode; + this.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes; + this.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes; + } + }, + // Analytics Actions logExperimentExposure({ userId, sessionKey, pageName, experiment }) { return globalMethods.callHttpClient({ diff --git a/src/store/store.spec.js b/src/store/store.spec.js index bd8a5f2a..9e30df93 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -4,6 +4,7 @@ import globalMethods from '@/global-methods.js'; import { getRandomString, getRandomGuid, getRandomInt, getRandomBoolean } from '@/helpers/data-generation.js'; import coverageStatuses from '@/constants/coverage-statuses.js'; import { endpoints } from '@/constants/endpoints'; +import { AppointmentTypeStrings } from '@/constants/schedule-constants'; describe('Store', () => { let store; @@ -396,6 +397,7 @@ describe('Store', () => { expect(globalMethods.callHttpClient).toHaveBeenCalled(); expect(store.payment.insuranceCoverage.isVerified).toBe(true); expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.NO_COMP); + expect(store.payment.insuranceCoverage.claimNumber).toBe(null); }); it('successful response with coverage => isVerified true and coverage status verified', async () => { @@ -422,11 +424,12 @@ describe('Store', () => { expect(globalMethods.callHttpClient).toHaveBeenCalled(); expect(store.payment.insuranceCoverage.isVerified).toBe(true); expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.VERIFIED); + expect(store.payment.insuranceCoverage.claimNumber).toBe(response.data.claimNumber); }); it('Call to client returns exception, resulting in object with error property being returned', async () => { // Arrange - expect.assertions(4); + expect.assertions(5); const error = 'this is the error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); @@ -439,6 +442,7 @@ describe('Store', () => { expect(globalMethods.callHttpClient).toHaveBeenCalled(); expect(store.payment.insuranceCoverage.isVerified).toBe(false); expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.PENDING); + expect(store.payment.insuranceCoverage.claimNumber).toBe(null); }); }); @@ -615,7 +619,10 @@ describe('Store', () => { firstName: getRandomString(6, 6), lastName: getRandomString(6, 6), emailAddress: getRandomString(6, 6), - phoneNumber: getRandomString(6, 6) + phoneNumber: getRandomString(6, 6), + address: { + state: getRandomString(2, 2) + } }; const policy = { policyNumber: getRandomString(6, 6), @@ -642,7 +649,8 @@ describe('Store', () => { policyFirstName: customer.firstName, policyLastName: customer.lastName, policyPhoneNumber: customer.phoneNumber, - policyEmail: customer.emailAddress + policyEmail: customer.emailAddress, + policyState: customer.address.state }), policyNumber: policy.policyNumber, policyZipCode: policy.policyZipCode, @@ -798,7 +806,8 @@ describe('Store', () => { startTime: getRandomString(6, 6), endTime: getRandomString(6, 6), routeCode: getRandomString(6, 6), - jobMaxMinutes: getRandomString(6, 6) + jobMaxMinutes: getRandomString(6, 6), + jobMinMinutes: getRandomString(6, 6) }; store.order.schedule = schedule; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); @@ -814,7 +823,8 @@ describe('Store', () => { startTime: schedule.startTime, endTime: schedule.endTime, routeCode: schedule.routeCode, - jobMaxMinutes: schedule.jobMaxMinutes + jobMaxMinutes: schedule.jobMaxMinutes, + jobMinMinutes: schedule.jobMinMinutes }) }) })); @@ -994,7 +1004,7 @@ describe('Store', () => { describe('updatePolicyITACFlag method', () => { it('updatePolicyITACFlag updates policy.isITAC flag in store', () => { - // Assert + // Arrange const moqIsITAC = getRandomBoolean(); // Act @@ -1004,4 +1014,88 @@ describe('Store', () => { expect(store.order.policy.isITAC).toEqual(moqIsITAC); }); }); + + describe('isMobileAppointment', () => { + it('Should return true for mobile appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ + appointmentType: AppointmentTypeStrings.MOBILE + }); + + // Assert + expect(store.isMobileAppointment).toBe(true); + }); + + it('Should return true for mobile-insurance appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ + appointmentType: AppointmentTypeStrings.MOBILE_NOT_ITAC + }); + + // Assert + expect(store.isMobileAppointment).toBe(true); + }); + + it('Should return false for non-mobile appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ + appointmentType: AppointmentTypeStrings.IN_SHOP + }); + + // Assert + expect(store.isMobileAppointment).toBe(false); + }); + + it('Should return false for null appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ appointmentType: null }); + + // Assert + expect(store.isMobileAppointment).toBe(false); + }); + }); + + describe('isDropoffAppointment', () => { + it('Should return true for drop-off appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ + appointmentType: AppointmentTypeStrings.DROP_OFF + }); + + // Assert + expect(store.isDropOffAppointment).toBe(true); + }); + + it('Should return false for non-drop-off appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ + appointmentType: AppointmentTypeStrings.MOBILE + }); + + // Assert + expect(store.isDropOffAppointment).toBe(false); + }); + + it('Should return false for null appointments', () => { + // Arrange + + // Act + store.updateServiceLocation({ appointmentType: null }); + + // Assert + expect(store.isDropOffAppointment).toBe(false); + }); + }); });