From c7b9137975eaf3b5b09134c6223ead76c7e4a948 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 18 Feb 2026 14:50:35 -0600 Subject: [PATCH 1/5] Initial work on moving components to the new bailout system. --- src/global-methods.spec.js | 24 ++- src/helpers/order-helper.js | 13 +- .../capability-questions.vue | 3 +- .../coverage-statement.spec.js | 19 +- .../coverage-statement/coverage-statement.vue | 17 +- .../duplicate-check/duplicate-check.spec.js | 8 +- .../duplicate-check/duplicate-check.vue | 16 +- src/layouts/entry-page/entry-page.vue | 54 +++--- .../molding-questions/molding-questions.vue | 3 +- src/layouts/part-questions/part-questions.vue | 3 +- src/layouts/payment-method/payment-method.vue | 23 +-- src/layouts/payment-return/payment-return.vue | 8 +- .../policy-vehicles/policy-vehicles.spec.js | 23 +-- .../policy-vehicles/policy-vehicles.vue | 51 ++--- .../provider-preference.spec.js | 7 +- .../provider-preference.vue | 4 +- src/layouts/tpa-search/tpa-search.spec.js | 11 +- src/layouts/tpa-search/tpa-search.vue | 6 +- src/layouts/tpa-submit/tpa-submit.vue | 16 +- .../vehicle-damage/vehicle-damage.spec.js | 20 +- src/layouts/vehicle-damage/vehicle-damage.vue | 22 +-- src/layouts/vehicle-lookup/vehicle-lookup.vue | 19 +- src/layouts/vehicle-parts/vehicle-parts.vue | 4 +- .../vehicle-selection/vehicle-selection.vue | 62 +----- src/layouts/vin-lookup/vin-lookup.spec.js | 42 +---- src/layouts/vin-lookup/vin-lookup.vue | 88 ++++----- src/layouts/welcome-page/welcome-page.spec.js | 13 +- src/layouts/welcome-page/welcome-page.vue | 72 +++---- src/mixins/vehicle-questions-mixin.js | 11 +- src/router/index.js | 176 +++++++++--------- .../router-constants/navigation-scenarios.js | 7 - src/router/router-constants/routing-table.js | 68 ------- src/router/router.spec.js | 13 +- src/store/index.js | 148 ++++++--------- 34 files changed, 374 insertions(+), 700 deletions(-) diff --git a/src/global-methods.spec.js b/src/global-methods.spec.js index fbb084ff..e5e82e45 100644 --- a/src/global-methods.spec.js +++ b/src/global-methods.spec.js @@ -21,7 +21,8 @@ global.$logger = { function setupMocksForHttpClient({ endpoint = null, isError = false, - additionalData = null + additionalData = null, + bailoutOnError = false }) { // Clear node module axios.mockClear(); @@ -57,7 +58,8 @@ function setupMocksForHttpClient({ return { endpoint, - logApiCall: true + logApiCall: true, + bailoutOnError }; } @@ -92,3 +94,21 @@ it('Global Methods - Call Http Client - Should Reject Promise', () => { expect(err.status).toEqual(500); }); }); + +it('Global Methods - Call Http Client - Rejected Promised - bailoutOnError: true - Calls global.bailoutOnAxiosError', () => { + // Arrange + const endpoint = 'https://mock.safelite.com'; + const httpArgs = setupMocksForHttpClient({ + endpoint, + isError: true, + bailoutOnError: true + }); + analyticsMixIn.methods.pushEventToGA = jest.fn(); + global.bailoutOnAxiosError = jest.fn(); + + // Act + globalMethods.callHttpClient(httpArgs).catch((err) => { + // Assert + expect(global.bailoutOnAxiosError).toHaveBeenCalledTimes(1); + }); +}); \ No newline at end of file diff --git a/src/helpers/order-helper.js b/src/helpers/order-helper.js index f1ab7c72..cbc5c890 100644 --- a/src/helpers/order-helper.js +++ b/src/helpers/order-helper.js @@ -4,8 +4,8 @@ import submitType from '@/constants/submit-type'; /* Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing */ -async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }) { - const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA }); +async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) { + const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }); if (savedSessionInfo) { store.setSaveSessionInfo(savedSessionInfo.data); } @@ -16,11 +16,11 @@ async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumber This will also set Referral information in the store after saving, and then update the cookie. To force synchronous behavior pass in 'true' for shouldAwaitSaveSessionQueue */ -export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false }) { +export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false, bailoutOnError = false }) { const store = useMainStore(); const saveSessionPromise = store.applicationUser.saveSessionPromise - ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA })) - : saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }); + ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError })) + : saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }); store.setSaveSessionPromise(saveSessionPromise); @@ -38,7 +38,8 @@ export async function submitWorkOrder({ submitType }) { store.resetSubmittedOrder(); await saveSession({ shouldAwaitSaveSessionQueue: true, - submitAfterSave: true + submitAfterSave: true, + bailoutOnError: true }); await store.createSubmittedOrder(submitType); } diff --git a/src/layouts/capability-questions/capability-questions.vue b/src/layouts/capability-questions/capability-questions.vue index c322a54b..e6dfd7bf 100644 --- a/src/layouts/capability-questions/capability-questions.vue +++ b/src/layouts/capability-questions/capability-questions.vue @@ -157,8 +157,7 @@ export default { this.navigateForward(this.partsOrQuestionsData, null); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index aa9c3058..9711a10b 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -63,7 +63,8 @@ const loadingModalStub = { function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() } }); @@ -578,7 +579,7 @@ describe('coverageStatement.vue', () => { undefined ); }); - test('If Verified ITAC, selected Cancel, navigate forward w/ CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', () => { + test('If Verified ITAC, selected Cancel, navigateBailout', () => { // Arrange const deductible = servicePrice + 1; const mainInitialState = { @@ -604,11 +605,7 @@ describe('coverageStatement.vue', () => { wrapper.vm.cancelClaim(); // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - undefined - ); + expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled() }); test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { // Arrange @@ -641,7 +638,7 @@ describe('coverageStatement.vue', () => { undefined ); }); - test('If No comp and selected other shop, navigate forward with CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP ', () => { + test('If No comp and selected other shop, navigateBailout ', () => { // Arrange const mainInitialState = { order: { @@ -666,11 +663,7 @@ describe('coverageStatement.vue', () => { wrapper.vm.cancelClaim(); // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - undefined - ); + expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled() }); }); describe('openCancelClaimModal', () => { diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index c1c7abbd..5a42a997 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -381,17 +381,6 @@ export default { // We only call the ITAC pricing endpoint if we are not repair or we are NoComp if (!this.isRepair || this.mainStore.isNoComp) { const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems) - .catch((err) => { - useMainStore().setBailout(bailoutMessage.pricingResponseError( - availableLineItems.map((li) => li.partNumber), - { - code: err.code, - message: err.message, - data: err.data - } - )); - this.navigateWithScenario(navigationScenarios.PRICING_LOOKUP_ERROR); - }); this.setBaseServiceLineItems(pricingResults); } }, @@ -402,8 +391,7 @@ export default { this.mainStore.updateIsSafeliteProvider(true); this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE); } else { - this.mainStore.setBailout(bailoutMessage.coverageStatementInvalidState()); - this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE); + this.$router.navigateBailout(bailoutMessage.coverageStatementInvalidState()) } }, navigateWithScenario(scenario) { @@ -457,8 +445,7 @@ export default { }, cancelClaim() { this.mainStore.updateIsSafeliteProvider(false); - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP); + this.$router.navigateBailout(bailoutMessage.RequestCallback()) } } }; diff --git a/src/layouts/duplicate-check/duplicate-check.spec.js b/src/layouts/duplicate-check/duplicate-check.spec.js index 10be35e2..95346c26 100644 --- a/src/layouts/duplicate-check/duplicate-check.spec.js +++ b/src/layouts/duplicate-check/duplicate-check.spec.js @@ -357,11 +357,15 @@ describe('duplicateCheck.vue', () => { useMainStore().loadSession = jest.fn().mockImplementation(() => Promise.reject(error)); // Act - await wrapper.vm.forwardButtonAction(); // Assert + expect.assertions(2); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toMatch(error); + } expect(wrapper.vm.mainStore.loadSession).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1); }); test('coverageType deductible and policy vehicles returned => CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', async () => { // Arrange diff --git a/src/layouts/duplicate-check/duplicate-check.vue b/src/layouts/duplicate-check/duplicate-check.vue index 1cac5a1e..3c4ead5f 100644 --- a/src/layouts/duplicate-check/duplicate-check.vue +++ b/src/layouts/duplicate-check/duplicate-check.vue @@ -140,18 +140,14 @@ export default { return; } - try { - const selectedReferral = - this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer); + const selectedReferral = + this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer); - if (selectedReferral) { - await this.mainStore.loadSession(selectedReferral); - } - } catch (err) { - console.error(`Error on loading session from duplicate check ${err}`); - } finally { - this.navigateForward(); + if (selectedReferral) { + await this.mainStore.loadSession(selectedReferral); } + + this.navigateForward(); }, navigateForward() { this.mainStore.updateDuplicateCheckVisited(true); diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue index e9880117..9893f024 100644 --- a/src/layouts/entry-page/entry-page.vue +++ b/src/layouts/entry-page/entry-page.vue @@ -151,37 +151,33 @@ export default { this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled; this.mainStore.issConfig.siteType = data.siteType; - try { - if (data.clientFlags) { - const clientFlags = JSON.parse(data.clientFlags); + if (data.clientFlags) { + const clientFlags = JSON.parse(data.clientFlags); - if (clientFlags.TPAEnabled) { - this.mainStore.issConfig.enableTPAFlow = true; - } - - if (clientFlags.ClientFullName != null) { - this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName; - } - - if (clientFlags.ClientDisplayName != null) { - this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName; - this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName); - } - - if (clientFlags.ClientPossessiveName != null) { - this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName; - } - - if (clientFlags.ClaimRegistrationRequired) { - this.mainStore.issConfig.isClaimRegistrationRequired = true; - } - - if (clientFlags.EnableNoCompQuote) { - this.mainStore.issConfig.enableNoCompQuote = true; - } + if (clientFlags.TPAEnabled) { + this.mainStore.issConfig.enableTPAFlow = true; + } + + if (clientFlags.ClientFullName != null) { + this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName; + } + + if (clientFlags.ClientDisplayName != null) { + this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName; + this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName); + } + + if (clientFlags.ClientPossessiveName != null) { + this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName; + } + + if (clientFlags.ClaimRegistrationRequired) { + this.mainStore.issConfig.isClaimRegistrationRequired = true; + } + + if (clientFlags.EnableNoCompQuote) { + this.mainStore.issConfig.enableNoCompQuote = true; } - } catch (e) { - console.error(`Error parsing client flags: ${e}`); } }, combineClientParameters(configParams, queryStringParams) { diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index cb0f4f04..44d6af81 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -148,8 +148,7 @@ export default { this.navigateForward(partsOrQuestions, null); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index 1230016a..9bed6345 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -148,8 +148,7 @@ export default { this.navigateForward(glassPartsForStore, null); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } }, }; diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index ba1f761b..676af3ef 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -260,25 +260,16 @@ export default { async forwardButtonAction() { useMainStore().savePaymentMethodChoice(this.paymentMethod); if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) { - try { - await submitWorkOrder({ submitType: submitType.SAFELITE }); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD, - this.$route - ); - } catch (error) { - useMainStore().setBailout(bailoutMessage.saveSessionError(error.data)); - this.$router.navigate( - this.navigationScenarios.SAVE_SESSION_FAILED, - this.$route, - { issPage: issPageValues.PAYMENT_METHOD } - ); - console.error(`error: response from submit work order:${error.message}`); - } + await submitWorkOrder({ submitType: submitType.SAFELITE }); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD, + this.$route + ); } else { await saveSession({ createWorkOrderNumberForPIA: true, - shouldAwaitSaveSessionQueue: true + shouldAwaitSaveSessionQueue: true, + bailoutOnError: true }); this.$router.navigate( diff --git a/src/layouts/payment-return/payment-return.vue b/src/layouts/payment-return/payment-return.vue index 09860d17..d4ba19a4 100644 --- a/src/layouts/payment-return/payment-return.vue +++ b/src/layouts/payment-return/payment-return.vue @@ -124,13 +124,7 @@ export default { }, async saveAndSubmitWorkOrder() { // Final work order submit after returning from pay in advance. - try { - await submitWorkOrder({ submitType: submitType.SAFELITE }); - } catch (error) { - console.error(`error: response from submit work order:${error.message}`); - this.navigateOnPayInAdvanceError(); - return; - } + await submitWorkOrder({ submitType: submitType.SAFELITE }); this.$router.navigate( this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS, diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index a4670b55..55f2c676 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -252,12 +252,12 @@ describe('policy-vehicles.vue', () => { ); test( - 'Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.', + 'Error in lookupVehicleByVin call => error thrown in forwardButtonAction.', async () => { // Arrange const { wrapper } = setupMocks({}); const lookupReturnValue = { error: true, status: 500, data: 'error' }; - wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(lookupReturnValue); + wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(Promise.reject(lookupReturnValue)); const vin = getRandomString(17, 17); await wrapper.setData({ @@ -270,21 +270,14 @@ describe('policy-vehicles.vue', () => { }); // Act - wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = { - 'bailout-page': { - bailoutCode: bailoutCode.VehicleVinLookupError - } - }; - await wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data)); - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - undefined, - {}, - {} - ); + expect.assertions(1); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toBe(lookupReturnValue); + } } ); diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index c459cefd..bd00493d 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -59,7 +59,6 @@ import vehicleSelectionOptions from '@/constants/vehicle-selection-options.js'; import endorsementOptions from '@/constants/endorsement-options.js'; import globalRules from '@/constants/global-rules.js'; import { useMainStore } from '@/store/index.js'; -import bailoutMessage from '@/constants/bailoutMessage'; import { deductibleForSelectedVehicle, endorsementsForSelectedVehicle, @@ -144,10 +143,6 @@ export default { }, repairWaivedForSelectedVehicle() { return repairWaivedForSelectedVehicle(this.selectedPolicyVehicle); - }, - selectedVehicle() { - const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin); - return vehicle; } }, watch: { @@ -158,24 +153,23 @@ export default { this.mainStore.resetVehicleState(); } else { // get vehicle details from selected VIN - const vehicle = await this.lookupVehicleByVin(value); + try { + const vehicle = await this.lookupVehicleByVin(value); - // handle error in case vehicle info doesn't come back for selected VIN - if (vehicle?.error === true) { + if (!vehicle?.data.canSafeliteService) { + this.displayNoServiceAlert = true; + return; + } + if (vehicle) { + // save selected vehicle to the store + useMainStore().updateVehicle({ + ...vehicle.data, + policyVehicleId: this.selectedPolicyVehicle?.id, + vin: value + }); + } + } catch (e) { this.mainStore.resetVehicleState(); - return; - } - if (!vehicle?.data.canSafeliteService) { - this.displayNoServiceAlert = true; - return; - } - if (vehicle) { - // save selected vehicle to the store - useMainStore().updateVehicle({ - ...vehicle.data, - policyVehicleId: this.selectedPolicyVehicle?.id, - vin: value - }); } } } @@ -217,11 +211,7 @@ export default { return this.navigateForward(); } - this.mainStore.setBailout(bailoutMessage.vehicleVinLookupError( - vehicle.vin, - vehicleLookupResponse.data - )); - return this.navigateForward(); + return; } useMainStore().updateVehicle({ @@ -242,14 +232,7 @@ export default { return this.navigateForward(); }, navigateForward() { - if (this.mainStore.isBailout) { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route, - {}, - {} - ); - } else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, this.$route, diff --git a/src/layouts/provider-preference/provider-preference.spec.js b/src/layouts/provider-preference/provider-preference.spec.js index 3fdcb495..0d93ed3a 100644 --- a/src/layouts/provider-preference/provider-preference.spec.js +++ b/src/layouts/provider-preference/provider-preference.spec.js @@ -20,7 +20,8 @@ jest.mock('@/helpers/cms-content-helper', () => ({ function setupMocks(mockApiResponses) { const mountOptions = getMountOptions({ router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() }, route: 'provider-preference' }); @@ -43,7 +44,7 @@ function setupMocks(mockApiResponses) { } describe('provider-preference.vue', () => { - test('Should navigate to tpa disabled route page when TPAOption selected and TPA Flow disabled', () => { + test('Should navigateBailout when TPAOption selected and TPA Flow disabled', () => { // Arrange const { wrapper } = setupMocks(); @@ -52,7 +53,7 @@ describe('provider-preference.vue', () => { wrapper.vm.findAnotherShopClicked(); // Test - expect(wrapper.vm.$router.navigate).toBeCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, 'provider-preference'); + expect(wrapper.vm.$router.navigateBailout).toBeCalled(); }); test('Should navigate to safelite flow when navigateWithTPARecalAnswer is called with SafeliteOption', () => { diff --git a/src/layouts/provider-preference/provider-preference.vue b/src/layouts/provider-preference/provider-preference.vue index 4400174e..27e38ff8 100644 --- a/src/layouts/provider-preference/provider-preference.vue +++ b/src/layouts/provider-preference/provider-preference.vue @@ -186,9 +186,7 @@ export default { this.scheduleWithTPA(); } } else { - this.mainStore.setBailout(bailoutMessage.TPANotEnabled()); - const scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED; - this.navigateForward(scenario); + this.$router.navigateBailout(bailoutMessage.TPANotEnabled()); } }, openStateSteeringModal() { diff --git a/src/layouts/tpa-search/tpa-search.spec.js b/src/layouts/tpa-search/tpa-search.spec.js index a888e60f..20a22153 100644 --- a/src/layouts/tpa-search/tpa-search.spec.js +++ b/src/layouts/tpa-search/tpa-search.spec.js @@ -53,7 +53,8 @@ const loaderStub = { function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() }, route: { query: { issPage: 'tpa-search' } @@ -787,7 +788,7 @@ describe('tpa-search.vue', () => { }); describe('needHelpLinkClick', () => { - test('sets bailout and navigates', () => { + test('navigatesBailout', () => { // Arrange const { wrapper } = getMountedComponent(); @@ -795,11 +796,7 @@ describe('tpa-search.vue', () => { wrapper.vm.needHelpLinkClick(); // Assert - expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalled(); - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( - navigationScenarios.CLICKED_NEED_HELP, - expect.anything() - ); + expect(wrapper.vm.$router.navigateBailout).toBeCalled(); }); }); diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 99241b1f..fd726728 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -358,11 +358,7 @@ export default { return getTpaProvidersResult?.data ?? []; }, needHelpLinkClick() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate( - this.navigationScenarios.CLICKED_NEED_HELP, - this.$route - ); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); }, async searchClick() { if (isNaN(this.tpaSearchValue)) { diff --git a/src/layouts/tpa-submit/tpa-submit.vue b/src/layouts/tpa-submit/tpa-submit.vue index d1a1e1ed..bd113db7 100644 --- a/src/layouts/tpa-submit/tpa-submit.vue +++ b/src/layouts/tpa-submit/tpa-submit.vue @@ -315,20 +315,8 @@ export default { }; }, async forwardButtonAction() { - try { - await submitWorkOrder({ submitType: submitType.TPA }).then(() => { - this.navigate(this.navigationScenarios.CLICKED_FORWARD); - }).catch((submitError) => { - this.mainStore.setBailout(bailoutMessage.saveSessionError(submitError.data)); - this.navigate( - this.navigationScenarios.SAVE_SESSION_FAILED, - this.$route, - { issPage: this.issPageValues.TPA_SUBMIT } - ); - }); - } catch (error) { - console.error(`error: response from submit work order:${error.message}`); - } + await submitWorkOrder({ submitType: submitType.TPA }) + this.navigate(this.navigationScenarios.CLICKED_FORWARD); }, navigate(scenario) { this.$router.navigate(scenario, this.$route); diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index db8ae50d..8bc26edb 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -139,7 +139,7 @@ describe('vehicle-damage.vue', () => { expect(vehicleQuestionsMixin.methods.getPartsOrQuestions).toHaveBeenCalledTimes(1); expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalledTimes(1); }); - test('Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', async () => { + test('Error in getPartsOrQuestions call => error thrown in forwardButtonAction', async () => { mountOptions.global.plugins = [createTestingPinia({ initialState: { main: { @@ -154,23 +154,21 @@ describe('vehicle-damage.vue', () => { } } })]; - mountOptions.data = () => ({ - hasBailedOut: true - }); + mountOptions.data = () => ({}); const wrapper = mount(VehicleDamageComponent, mountOptions); - const siteFooterWrapper = wrapper.getComponent({ ref: 'siteFooter' }); const partsQuestionsErrorResponse = { error: 'Error getting parts' }; vehicleQuestionsMixin.methods.getPartsOrQuestions.mockImplementation(() => ( - partsQuestionsErrorResponse + Promise.reject(partsQuestionsErrorResponse) )); - siteFooterWrapper.vm.$emit('forwardClicked'); - await flushPromises(); - expect(mockRouter.navigate).toHaveBeenCalledTimes(1); - expect(mockRouter.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, mockRoute); + expect.assertions(1); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toBe(partsQuestionsErrorResponse); + } }); }); diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index b486f90e..a27e3655 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -189,8 +189,7 @@ export default { this.getPassengerSideReplaceOptionsFromStore() }, selectedWindshieldOptions: this.getWindshieldOptionsFromStore(), - selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(), - hasBailedOut: false + selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore() }; }, computed: { @@ -429,23 +428,12 @@ export default { // If vin already exists or not replacing windshield, get parts/questions and navigate forward const partsOrQuestionsResponse = await this.getPartsOrQuestions(); - if (partsOrQuestionsResponse.error) { - this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); - window.console.error('Error on retrieving PartsOrQuestions'); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } // Comes from vehicleQuestionsMixin.navigateForward() - if (!this.hasBailedOut) { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); } else { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, diff --git a/src/layouts/vehicle-lookup/vehicle-lookup.vue b/src/layouts/vehicle-lookup/vehicle-lookup.vue index b322b7c8..acbf58a0 100644 --- a/src/layouts/vehicle-lookup/vehicle-lookup.vue +++ b/src/layouts/vehicle-lookup/vehicle-lookup.vue @@ -113,21 +113,10 @@ export default { break; case vinLookupMethodSelections.NOVIN: const partsOrQuestionsResponse = await this.getPartsOrQuestions(); - if (partsOrQuestionsResponse.error) { - this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); - window.console.error('Error on retrieving PartsOrQuestions'); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - else { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); break; default: break; diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue index b951f777..a49e8be4 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.vue +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -64,6 +64,7 @@ import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { useMainStore } from '@/store'; import widgetFields from '@/constants/cms-widget-fields'; +import bailoutMessage from '@/constants/bailoutMessage'; export default { name: 'vehicle-parts', @@ -213,8 +214,7 @@ export default { }); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue index 1f56fec3..1cfb7f4d 100644 --- a/src/layouts/vehicle-selection/vehicle-selection.vue +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -206,77 +206,25 @@ export default { }, navigateForward() { - this.mainStore.setVehicle().then( - () => { - if (this.mainStore.isBailout) { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - return; - } - + this.mainStore.setVehicle().then(() => { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD, this.$route ); - }, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, this.mainStore.vehicle.style, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); } ); }, async updateYearValues() { - return this.mainStore.getVehicleYears().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(null, null, null, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleYears(); }, async updateMakeValues() { - return this.mainStore.getVehicleMakes().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, null, null, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleMakes(); }, async updateModelValues() { - return this.mainStore.getVehicleModels().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, null, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleModels(); }, async updateStyleValues() { - return this.mainStore.getVehicleStyles().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleStyles(); }, resetAlert() { this.displayNoServiceAlert = false; diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js index 0376eaa3..b5808d77 100644 --- a/src/layouts/vin-lookup/vin-lookup.spec.js +++ b/src/layouts/vin-lookup/vin-lookup.spec.js @@ -148,7 +148,8 @@ const mockRoute = { }; const mockRouter = { navigate: jest.fn(), - navigateWithSpinner: jest.fn() + navigateWithSpinner: jest.fn(), + navigateBailout: jest.fn() }; const maska = jest.fn(); jest.mock('@/helpers/damage-helper', () => ({ @@ -553,45 +554,6 @@ describe('vin-lookup.vue', () => { ); }); }); - test( - 'Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', - async () => { - const user = userEvent.setup(); - mountOptions.global.stubs.vinQuestion = false; - - mountOptions.data = () => ({ - vinWithNonMatchingCarId: false, - isCarIdDifferentFromTheStore: false, - vin: mockValidVin, - hasBailedOut: true - }); - - getPartsOrQuestions.mockResponse = partsOrQuestionsErrorMockResponse; - - jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) - .mockResolvedValue(lookupVehicleByVin.mockResponse); - jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName) - .mockResolvedValue(getPartsOrQuestions.mockResponse); - - const { container } = render(VinLookupComponent, mountOptions); - - const vinInput = container.querySelector(vinInputSelector); - await user.type(vinInput, mockValidVin); - - const continueButton = container.querySelector(continueButtonQuerySelector); - await user.click(continueButton); - - await flushPromises(); - await waitFor(() => { - expect(mockRouter.navigate).toHaveBeenCalledTimes(1); - expect(mockRouter.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - mockRoute - ); - }); - } - ); }); }); }); diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index 3bb0d637..05582873 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -112,8 +112,7 @@ export default { vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(), vin, forwardButtonCarStyle: '', - vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId(), - hasBailedOut: false + vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId() }; }, computed: { @@ -176,35 +175,37 @@ export default { showIssLoadingModal(true); if (this.needToLookupVehicle) { - const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin); + try { + const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin); - if (vehicleLookupResponse.error) { - this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; - this.mainStore.setBailout(bailoutMessage.vehicleNotFound(this.vin)); - this.resetVehicleFromLookup(); - // Temp solution to turn on 'disabled' style on the Continue button - // because the form itself actually passes its client-side validation. - // SSR-189 Scenario #4. - this.$refs.siteFooter.enableForwardAction(); - showIssLoadingModal(false); - return; - } - - if (!vehicleLookupResponse.data.canSafeliteService) { - this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE; - this.resetVehicleFromLookup(); - this.$refs.siteFooter.disableForwardButton(); - showIssLoadingModal(false); - return; - } - - // Add vin bcs the response from the service doesn't contain vin - this.vehicleFromLookup = Object.assign( - vehicleLookupResponse.data, - { - vin: this.vin + if (!vehicleLookupResponse.data.canSafeliteService) { + this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE; + this.resetVehicleFromLookup(); + this.$refs.siteFooter.disableForwardButton(); + showIssLoadingModal(false); + return; } - ); + + // Add vin bcs the response from the service doesn't contain vin + this.vehicleFromLookup = Object.assign( + vehicleLookupResponse.data, + { + vin: this.vin + } + ); + } catch (e) { + if (e.status === 404) { + this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; + this.resetVehicleFromLookup(); + // Temp solution to turn on 'disabled' style on the Continue button + // because the form itself actually passes its client-side validation. + // SSR-189 Scenario #4. + this.$refs.siteFooter.enableForwardAction(); + showIssLoadingModal(false); + return; + } + throw e; + } } if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) { @@ -259,34 +260,15 @@ export default { } const partsOrQuestionsResponse = await this.getPartsOrQuestions(); - if (partsOrQuestionsResponse.error) { - this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); - window.console.error('Error on retrieving PartsOrQuestions'); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } // Comes from vehicleQuestionsMixin.navigateForward() - if (!this.hasBailedOut) { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); }, async lookupVehicleByVin(vin) { - try { - return await this.mainStore.lookupVehicleByVin(vin); - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; - } + return this.mainStore.lookupVehicleByVin(vin); }, resetActiveAlert() { this.activeVehicleLookupAlertType = null; diff --git a/src/layouts/welcome-page/welcome-page.spec.js b/src/layouts/welcome-page/welcome-page.spec.js index 4748a9b0..39fd8284 100644 --- a/src/layouts/welcome-page/welcome-page.spec.js +++ b/src/layouts/welcome-page/welcome-page.spec.js @@ -72,7 +72,8 @@ function setupMocks({ const mockDataMountOptions = { ...mountOptionsMockData, router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() } }; @@ -166,7 +167,7 @@ describe('navigation', () => { const { wrapper } = setupMocks({}); wrapper.vm.mainStore.getDuplicateReferrals = jest.fn().mockImplementation(() => Promise.resolve({})); wrapper.vm.mainStore.applicationUser.duplicateOrders = [{ test: 'a' }]; - wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject()); + wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject({ isAxiosError: false })); // Act await wrapper.vm.forwardButtonAction(); @@ -316,10 +317,14 @@ describe('navigation', () => { })); // Act - await wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + expect.assertions(1); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toMatch(error); + } }); }); }); diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue index 6520a930..235f510b 100644 --- a/src/layouts/welcome-page/welcome-page.vue +++ b/src/layouts/welcome-page/welcome-page.vue @@ -303,54 +303,47 @@ export default { }, methods: { async forwardButtonAction() { - try { - this.mainStore.updatePolicyData(this.welcomePageModel); - const promises = []; - promises.push(this.configureZip().then(async () => await this.mainStore.getBillToInfo())); + this.mainStore.updatePolicyData(this.welcomePageModel); - // Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page. - if (this.mainStore.order.visitedDuplicateCheckPage) { - this.mainStore.clearDuplicateOrders(); - } - - // Skip duplicate check if loaded from cookie or already visited duplicate check page. - if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) { - promises.push(this.mainStore.getDuplicateReferrals()); - } - promises.push(this.mainStore.getCoveragePolicyInfo()); - await Promise.allSettled(promises); - } catch (e) { - console.error(e); - // TODO: Bailout? - } finally { - if (!this.displayInvalidZipAlert) { - await saveSession({ shouldAwaitSaveSessionQueue: true }) - .catch((error) => { - this.mainStore.setBailout(bailoutMessage.saveSessionError(error.data)); - }) - .finally(() => this.navigateForward()); - } + // We want to await this separately. It's quick and if this has an invalid zip we don't want to be waiting on the slower API calls + await this.configureZip(); + if (this.displayInvalidZipAlert) { + showIssLoadingModal(false); + return; } + + // Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page. + if (this.mainStore.order.visitedDuplicateCheckPage) { + this.mainStore.clearDuplicateOrders(); + } + + const promises = [ + // Suppress error from API call we can try again later in the flow + this.mainStore.getBillToInfo() + ]; + // Skip duplicate check if loaded from cookie or already visited duplicate check page. + if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) { + promises.push(this.mainStore.getDuplicateReferrals()); + } + promises.push(this.mainStore.getCoveragePolicyInfo()); + await Promise.all(promises); + await saveSession({shouldAwaitSaveSessionQueue: true}) + .catch((error) => this.mainStore.setBailout(bailoutMessage.saveSessionError(error))); + this.navigateForward(); }, async configureZip() { try { this.displayInvalidZipAlert = false; await this.mainStore.validateZip({ zip: this.welcomePageModel.policyZipCode }); - return Promise.resolve(); } catch (e) { + if (e.isAxiosError) { + throw e; + } this.displayInvalidZipAlert = true; - return Promise.reject(e); } }, navigateForward() { - if (this.mainStore.isBailout) { - this.$router.navigate( - this.navigationScenarios.SAVE_SESSION_FAILED, - this.$route, - {}, - { [routerParams.SKIP_SAVE_SESSION]: true } - ); - } else if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false) + if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false) && !this.answeredContinueModal) { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES, @@ -445,12 +438,7 @@ export default { .then((response) => { this.answeredContinueModal = true; if (response && !response.provider?.isSafeliteProvider) { - this.mainStore.setBailout(bailoutMessage.SafeliteNotTheProvider()); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); + this.$router.navigateBailout(bailoutMessage.SafeliteNotTheProvider()); } else { this.updateWelcomePageModel(response); } diff --git a/src/mixins/vehicle-questions-mixin.js b/src/mixins/vehicle-questions-mixin.js index d5c492d8..6efed9ee 100644 --- a/src/mixins/vehicle-questions-mixin.js +++ b/src/mixins/vehicle-questions-mixin.js @@ -353,16 +353,7 @@ export default { }, async getPartsOrQuestions() { - try { - return await useMainStore().getPartsOrQuestions(); - } catch (responseError) { - return { - error: { - status: responseError.status, - data: responseError.data - } - }; - } + return useMainStore().getPartsOrQuestions(); }, // Can't use `this` because navigateForward is also called from vin-pages-mixin diff --git a/src/router/index.js b/src/router/index.js index cd77544f..c6a18493 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -25,100 +25,95 @@ const routes = [ path: '/', name: 'root', async beforeEnter(to, from, next) { - try { - const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage; - const fromQueryPage = from.query?.issPage; + const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage; + const fromQueryPage = from.query?.issPage; - if ((issPageToUse === issPageValues.ACCESS_DENIED + if ((issPageToUse === issPageValues.ACCESS_DENIED || (issPageToUse !== issPageValues.ENTRY_PAGE && !useMainStore().issConfig.parentAccountNumber)) - && process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost' - ) { - return await GoToAccessIsDenied(next); + && process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost' + ) { + return await GoToAccessIsDenied(next); + } + + // Do not run these for the main entry page - as it is not part of the user flow. + if (issPageToUse !== issPageValues.ENTRY_PAGE) { + if (analyticsMixin.methods.noSession()) { + await analyticsMixin.methods.initSession(); + } else { + updateSessionIdCookie(); } - // Do not run these for the main entry page - as it is not part of the user flow. - if (issPageToUse !== issPageValues.ENTRY_PAGE) { - if (analyticsMixin.methods.noSession()) { - await analyticsMixin.methods.initSession(); - } else { - updateSessionIdCookie(); - } + await runExperiments(issPageToUse); // fmg has this further down + } - await runExperiments(issPageToUse); // fmg has this further down + // Intercept all navigation if a submitted order exists in storage + if (useMainStore().hasSubmittedOrder()) { + if (to.query.issPage !== issPageValues.ENTRY_PAGE) { + return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder()); + } + } + + // If the saved session has timed out, clear the session, execute 404 logic. + if (getISSCookie() !== null && !isSavedSessionStillActive()) { + // await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); + await GoToStartOn404(next); + } + + // Process ISS cookie. + // Skip if Entry Page or Refreshing Welcome page + if (issPageToUse !== issPageValues.ENTRY_PAGE + && !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) { + updateOrCreateISSCookie(); + } + + if (router.hasRoute(issPageToUse)) { + // Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function. + let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components; + + // If the component hasn't been loaded fully, load it before we check prerequisites. + if (component.default.methods === undefined) { + component = await component.default(); } - // Intercept all navigation if a submitted order exists in storage - if (useMainStore().hasSubmittedOrder()) { - if (to.query.issPage !== issPageValues.ENTRY_PAGE) { - return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder()); - } - } - - // If the saved session has timed out, clear the session, execute 404 logic. - if (getISSCookie() !== null && !isSavedSessionStillActive()) { - // await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); + if (!arePagePrerequisitesValid(component)) { await GoToStartOn404(next); } - // Process ISS cookie. - // Skip if Entry Page or Refreshing Welcome page - if (issPageToUse !== issPageValues.ENTRY_PAGE - && !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) { - updateOrCreateISSCookie(); - } - - if (router.hasRoute(issPageToUse)) { - // Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function. - let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components; - - // If the component hasn't been loaded fully, load it before we check prerequisites. - if (component.default.methods === undefined) { - component = await component.default(); - } - - if (!arePagePrerequisitesValid(component)) { - await GoToStartOn404(next); - } - - return next({ name: issPageToUse, query: to.query, params: to.params }); - } - - const routeData = await GetRouteInfoFromPageName(issPageToUse); - - if (routeData[0].name.toLowerCase() === 'error') { - throw new Error('Page not found!'); - } - - // Add our dynamic route. - router.addRoute({ - path: routeData[0].path, // Always the same path, because we control it with query strings. - name: routeData[0].name, - component: routeData[0].component - }); - - // Call the next components arePagePrerequisitesValid method before load. - // If it returns false, use the 404 logic. - const nextComponent = await router - .getRoutes() - .filter((x) => x.name === routeData[0].name)[0] - .components.default(); - - if (!arePagePrerequisitesValid(nextComponent)) { - const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.'; - const tempMsgHeadline = `${issPageToUse}: pre-req failed...`; - await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline); - } - - // Assign current query string parameters, as well as our issPage one. - next({ - name: routeData[0].name, - query: Object.assign(to.query, { issPage: routeData[0].name }), - params: to.params - }); - } catch (error) { - window.console.warn(error); - await GoToStartOn404(next); + return next({name: issPageToUse, query: to.query, params: to.params}); } + + const routeData = await GetRouteInfoFromPageName(issPageToUse); + + if (routeData[0].name.toLowerCase() === 'error') { + throw new Error('Page not found!'); + } + + // Add our dynamic route. + router.addRoute({ + path: routeData[0].path, // Always the same path, because we control it with query strings. + name: routeData[0].name, + component: routeData[0].component + }); + + // Call the next components arePagePrerequisitesValid method before load. + // If it returns false, use the 404 logic. + const nextComponent = await router + .getRoutes() + .filter((x) => x.name === routeData[0].name)[0] + .components.default(); + + if (!arePagePrerequisitesValid(nextComponent)) { + const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.'; + const tempMsgHeadline = `${issPageToUse}: pre-req failed...`; + await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline); + } + + // Assign current query string parameters, as well as our issPage one. + next({ + name: routeData[0].name, + query: Object.assign(to.query, {issPage: routeData[0].name}), + params: to.params + }); return null; } } @@ -177,7 +172,7 @@ router.afterEach(async (to, from) => { const skipSaveSession = !!router.options.history.state[routerParams.SKIP_SAVE_SESSION]; if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) { - await saveSession({}); + await saveSession({ bailoutOnError: from.name === issPageValues.ENTRY_PAGE}); } if (to.query.issPage !== issPageValues.ENTRY_PAGE) { @@ -350,17 +345,12 @@ router.navigateBailout = (bailoutData = null) => { // Get navigation map depending on the scenario and the current 'page' you're on. function getNavigationMap(scenario, currentRoute) { const issPageValue = currentRoute.query.issPage; - try { - const matchedQueryValue = routingTable(useMainStore()) - .filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0); + const matchedQueryValue = routingTable(useMainStore()) + .filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0); - const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined; + const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined; - return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined; - } catch (e) { - window.console.error(e); - return undefined; - } + return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined; } async function GoToAccessIsDenied(next) { diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index f0603b96..07a012ed 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -12,7 +12,6 @@ const navigationScenarios = Object.freeze({ CLICKED_FORWARD_POLICY_UNVERIFIED: 'CLICKED_FORWARD_POLICY_UNVERIFIED', CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES', CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', - SAVE_SESSION_FAILED: 'SAVE_SESSION_FAILED', // Duplicate Check CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE: 'CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE', @@ -70,11 +69,8 @@ const navigationScenarios = Object.freeze({ // Coverage Statement CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR', - CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE', - PRICING_LOOKUP_ERROR: 'PRICING_LOOKUP_ERROR', // TPA Search - CLICKED_NEED_HELP: 'CLICKED_NEED_HELP', CLICKED_FORWARD_WITH_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_SAFELITE_SHOP', CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', @@ -88,7 +84,6 @@ const navigationScenarios = Object.freeze({ // Provider Preference CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE', CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED', - CLICKED_FORWARD_WITH_TPA_DISABLED: 'CLICKED_FORWARD_WITH_TPA_DISABLED', CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES: 'CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES', // Service Package @@ -112,8 +107,6 @@ const navigationScenarios = Object.freeze({ PAY_IN_ADVANCE_SUCCESS: 'PAY_IN_ADVANCE_SUCCESS', // Bailout - CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT', - CLICKED_NEED_HELP_WITH_BAILOUT: 'CLICKED_NEED_HELP_WITH_BAILOUT', BAILOUT: 'BAILOUT' }); diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 3751060c..7911b5c6 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -14,10 +14,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD, destinationIssPageValue: issPageValues.POLICY_HOLDER_DETAILS - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -55,10 +51,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, destinationIssPageValue: issPageValues.VEHICLE_LOOKUP - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -141,14 +133,6 @@ const routingTable = () => [ { scenario: issPageValues.VEHICLE_LOOKUP, destinationIssPageValue: issPageValues.VEHICLE_LOOKUP - }, - { - scenario: issPageValues.BAILOUT_PAGE, // TODO is this a bug - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -215,10 +199,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -256,10 +236,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -293,10 +269,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -455,14 +427,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES, destinationIssPageValue: issPageValues.POLICY_VEHICLES - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.SAVE_SESSION_FAILED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -539,10 +503,6 @@ const routingTable = () => [ scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, destinationIssPageValue: issPageValues.VIN_LOOKUP }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, { scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS @@ -591,18 +551,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, destinationIssPageValue: issPageValues.SCHEDULE_PAGE - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.PRICING_LOOKUP_ERROR, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -620,10 +568,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED, destinationIssPageValue: issPageValues.TPA_SEARCH - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -692,10 +636,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_PAY_NOW, destinationIssPageValue: issPageValues.PAYMENT_PAGE - }, - { - scenario: navigationScenarios.SAVE_SESSION_FAILED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -768,10 +708,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD, destinationIssPageValue: issPageValues.TPA_CONFIRMATION - }, - { - scenario: navigationScenarios.SAVE_SESSION_FAILED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -798,10 +734,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, destinationIssPageValue: issPageValues.TPA_SUBMIT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, diff --git a/src/router/router.spec.js b/src/router/router.spec.js index ee35151a..675fa7fa 100644 --- a/src/router/router.spec.js +++ b/src/router/router.spec.js @@ -4,6 +4,8 @@ import issPageValues from '@/router/router-constants/issPage-values'; import { createApp } from 'vue'; import { createPinia } from 'pinia'; import App from '@/App.vue'; +import bailoutMessage from '@/constants/bailoutMessage'; +import { useMainStore } from '@/store'; describe('Router', () => { beforeAll(() => { @@ -37,18 +39,15 @@ describe('Router', () => { expect(router.push.mock.calls[0][0].state).toBe(parameters); }); - it('Should route from CAPABILITY_QUESTIONS to BAILOUT_PAGE on CLICKED_NEED_HELP_WITH_BAILOUT', () => { - const scenario = navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT; - const currentRoute = { query: { issPage: issPageValues.CAPABILITY_QUESTIONS } }; - + it('Should set bailout and navigate to bailout page when calling navigateBailout', () => { + // Arrange router.push = jest.fn(); // Act - router.navigate(scenario, currentRoute); + router.navigateBailout(bailoutMessage.unknown({})); // Assert - expect(router.push).toHaveBeenCalled(); - expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.BAILOUT_PAGE); + expect(useMainStore().isBailout).toBeTruthy() }); }); diff --git a/src/store/index.js b/src/store/index.js index 806ce1e6..7bcd88ab 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,7 +1,6 @@ import { defineStore } from 'pinia'; import applicationConfig from '@/constants/application-config'; import bailoutCode from '@/constants/bailoutCode'; -import bailoutMessage from '@/constants/bailoutMessage'; import coverageStatuses from '@/constants/coverage-statuses'; import coverageType from '@/constants/coverage-type'; import damageLocationsSelected from '@/constants/damage-locations-selected'; @@ -22,11 +21,7 @@ import { noCoverageForSelectedVehicle, repairWaivedForSelectedVehicle } from '@/helpers/policy-vehicle-helper'; -import { - buildURLSearchParams, - getPartNumbersListForQueryString, - getTaxLineItemQueryString -} from '@/helpers/querystring-helper'; +import { buildURLSearchParams, getPartNumbersListForQueryString } from '@/helpers/querystring-helper'; import { getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper'; import { getDateForSavedSessionTimeout } from '@/helpers/session-helper'; import issPageValues from '@/router/router-constants/issPage-values'; @@ -534,20 +529,11 @@ export const useMainStore = defineStore({ }); }, getIsVinbyAddressPermissible() { - try { - const response = globalMethods.callHttpClient({ - method: endpoints.IsVinbyAddressPermissible.method, - endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, - payload: {} - }); - return response; - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; - } + return globalMethods.callHttpClient({ + method: endpoints.IsVinbyAddressPermissible.method, + endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, + payload: {} + }); }, async getCoveragePolicyInfo() { const { order, issConfig, applicationUser } = this; @@ -561,49 +547,43 @@ export const useMainStore = defineStore({ this.applicationUser.coverageAttempts += 1; console.log(`Coverage lookup attempt #${this.applicationUser.coverageAttempts}. Max attempts allowed: 10.`); - try { - const response = await globalMethods.callHttpClient({ - method: endpoints.CoveragePolicyInfo.method, - endpoint: endpoints.CoveragePolicyInfo.url, - payload: { - accountNumber: order.parentAccountNumber?.toString(), - policyNumber: policy.policyNumber, - dateOfLoss: policy.dateOfLoss, - zipCode: policy.policyZipCode, - referralCorrelationId: order.referralCorrelationId - } - }); - - const responsePolicy = response?.data?.policies?.[0]; - if (responsePolicy) { - this.updateCoverageType(coverageType.Deductible); - const insured = responsePolicy.insureds?.[0]; - - // populate parent account number - if (response.data.accountNumber) { - order.parentAccountNumber = parseInt(response.data.accountNumber, 10); - } - - // populate policy holder details from policy lookup - order.customer.address.streetAddress = insured?.address; - order.customer.address.city = insured?.city; - order.customer.address.state = insured?.state; - order.customer.address.zipCode = insured?.zipCode?.toString(); - order.customer.firstName = insured?.firstName; - order.customer.lastName = insured?.lastName; - - // populate additional fields - order.policy.policyData = responsePolicy.policyData; - - // populate vehicles - order.policy.vehicles = responsePolicy.vehicles ?? []; - } else { - this.updateCoverageType(coverageType.NONE); + const response = await globalMethods.callHttpClient({ + method: endpoints.CoveragePolicyInfo.method, + endpoint: endpoints.CoveragePolicyInfo.url, + payload: { + accountNumber: order.parentAccountNumber?.toString(), + policyNumber: policy.policyNumber, + dateOfLoss: policy.dateOfLoss, + zipCode: policy.policyZipCode, + referralCorrelationId: order.referralCorrelationId } - return Promise.resolve(); - } catch (e) { + }); + + const responsePolicy = response?.data?.policies?.[0]; + if (responsePolicy) { + this.updateCoverageType(coverageType.Deductible); + const insured = responsePolicy.insureds?.[0]; + + // populate parent account number + if (response.data.accountNumber) { + order.parentAccountNumber = parseInt(response.data.accountNumber, 10); + } + + // populate policy holder details from policy lookup + order.customer.address.streetAddress = insured?.address; + order.customer.address.city = insured?.city; + order.customer.address.state = insured?.state; + order.customer.address.zipCode = insured?.zipCode?.toString(); + order.customer.firstName = insured?.firstName; + order.customer.lastName = insured?.lastName; + + // populate additional fields + order.policy.policyData = responsePolicy.policyData; + + // populate vehicles + order.policy.vehicles = responsePolicy.vehicles ?? []; + } else { this.updateCoverageType(coverageType.NONE); - return Promise.reject(e); } }, clearDuplicateOrders() { @@ -1360,7 +1340,8 @@ export const useMainStore = defineStore({ endpoint: endpoints.LookupVehicleByVin.url, payload: { vin - } + }, + bailoutOnError: false }); }, @@ -1392,7 +1373,7 @@ export const useMainStore = defineStore({ this.applicationUser.crmCustomerId = response.crmCustomerId.toString(); }, - saveSession({ submitAfterSave, createWorkOrderNumberForPIA }) { + saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) { const { vehicle, damage, policy, customer, contactInfo, payment, lineItems, serviceLocation, schedule, insuranceCoverage } = this.order; @@ -1544,7 +1525,7 @@ export const useMainStore = defineStore({ method: endpoints.SaveSession.method, endpoint: endpoints.SaveSession.url, payload, - bailoutOnError: false + bailoutOnError }).then((response) => { if (loadedFromDupeCheck) { this.order.loadedSessionClearedPreviousData = true; @@ -2461,6 +2442,7 @@ export const useMainStore = defineStore({ populateInitialState(forceReset) { if (!sessionStorage.getItem(storeId) || forceReset) { this.$state = getDefaultState(); + sessionStorage.removeItem(webStorageConstants.SUBMITTED_ORDER); } }, @@ -2699,34 +2681,26 @@ export const useMainStore = defineStore({ async getBillToInfo(componentProviderNumber = null) { const { order, issConfig } = this; - try { - const params = new URLSearchParams({ - parentAccountNumber: order.parentAccountNumber.toString(), - providerNumber: componentProviderNumber || this.providerNumber, - typeOfClaim: 'GLASS ONLY', - lineOfBusiness: 'PERSONAL', - isItac: this.isITAC - }); + const params = new URLSearchParams({ + parentAccountNumber: order.parentAccountNumber.toString(), + providerNumber: componentProviderNumber || this.providerNumber, + typeOfClaim: 'GLASS ONLY', + lineOfBusiness: 'PERSONAL', + isItac: this.isITAC + }); - const response = await globalMethods.callHttpClient({ - method: endpoints.GetBillToInfo.method, - endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}` - }); + const response = await globalMethods.callHttpClient({ + method: endpoints.GetBillToInfo.method, + endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`, + bailoutOnError: false + }); - const billToInfo = response.data; - if (billToInfo != null) { - issConfig.billToAccountNumber = billToInfo.toString(); - return Promise.resolve(); - } - - return Promise.reject(new Error('Invalid billToInfo')); - } catch (e) { - return Promise.reject(e); - } + const billToInfo = response.data; + issConfig.billToAccountNumber = billToInfo.toString(); }, async validateClientTag(clientTag) { - return await globalMethods.callHttpClient({ + return globalMethods.callHttpClient({ method: endpoints.ValidateClientTag.method, endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}` }); From 991ef0ebb2974289faff868c2bd2a242c5c0e76a Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Wed, 18 Feb 2026 14:52:10 -0600 Subject: [PATCH 2/5] Missed file --- src/layouts/welcome-page/welcome-page.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue index 235f510b..e75d2c37 100644 --- a/src/layouts/welcome-page/welcome-page.vue +++ b/src/layouts/welcome-page/welcome-page.vue @@ -177,6 +177,7 @@ import { getPropertyCaseInsensitive } from '@/helpers/object-helper'; import { saveSession } from '@/helpers/order-helper'; import { getISSCookie } from '@/helpers/cookie-helper.js'; import bailoutMessage from '@/constants/bailoutMessage'; +import showIssLoadingModal from '@/helpers/loading-modal-helper'; // define validation rules defineRule( From 583a675fa37a84a72e04a1d79cc5c19df660cb14 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Thu, 19 Feb 2026 09:18:57 -0600 Subject: [PATCH 3/5] Copilot changes --- src/global-methods.js | 2 +- .../coverage-statement.spec.js | 4 +- .../coverage-statement/coverage-statement.vue | 6 +- .../policy-vehicles/policy-vehicles.spec.js | 2 +- .../policy-vehicles/policy-vehicles.vue | 102 +++++++++--------- src/layouts/welcome-page/welcome-page.vue | 3 +- src/router/router.spec.js | 2 +- src/store/index.js | 7 +- 8 files changed, 60 insertions(+), 68 deletions(-) diff --git a/src/global-methods.js b/src/global-methods.js index 280f318d..7a6fcf33 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -97,7 +97,7 @@ export default { global.$logger.logError(`${method}: ${endpoint}: ${error.message}`, error.response); if (bailoutOnError && global.bailoutOnAxiosError !== undefined) { - global.bailoutOnAxiosError({ url, error }); + global.bailoutOnAxiosError({ method, url, payload, error }); } } return reject(error.response); diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index 9711a10b..88cde655 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -605,7 +605,7 @@ describe('coverageStatement.vue', () => { wrapper.vm.cancelClaim(); // Assert - expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled() + expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled(); }); test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { // Arrange @@ -663,7 +663,7 @@ describe('coverageStatement.vue', () => { wrapper.vm.cancelClaim(); // Assert - expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled() + expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled(); }); }); describe('openCancelClaimModal', () => { diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 5a42a997..7a8be805 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -380,7 +380,7 @@ export default { // We only call the ITAC pricing endpoint if we are not repair or we are NoComp if (!this.isRepair || this.mainStore.isNoComp) { - const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems) + const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems); this.setBaseServiceLineItems(pricingResults); } }, @@ -391,7 +391,7 @@ export default { this.mainStore.updateIsSafeliteProvider(true); this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE); } else { - this.$router.navigateBailout(bailoutMessage.coverageStatementInvalidState()) + this.$router.navigateBailout(bailoutMessage.coverageStatementInvalidState()); } }, navigateWithScenario(scenario) { @@ -445,7 +445,7 @@ export default { }, cancelClaim() { this.mainStore.updateIsSafeliteProvider(false); - this.$router.navigateBailout(bailoutMessage.RequestCallback()) + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index 55f2c676..62c6b526 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -285,7 +285,7 @@ describe('policy-vehicles.vue', () => { async () => { // Arrange const { wrapper } = setupMocks({}); - wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 404 }); + wrapper.vm.lookupVehicleByVin = jest.fn().mockRejectedValue({ isAxiosError: true, status: 404 }); const vin = getRandomString(17, 17); await wrapper.setData({ diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index bd00493d..c3b6c8ee 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -151,26 +151,26 @@ export default { if (value === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { // clear previously selected vehicle and image this.mainStore.resetVehicleState(); - } else { - // get vehicle details from selected VIN - try { - const vehicle = await this.lookupVehicleByVin(value); + return; + } - if (!vehicle?.data.canSafeliteService) { - this.displayNoServiceAlert = true; - return; - } - if (vehicle) { - // save selected vehicle to the store - useMainStore().updateVehicle({ - ...vehicle.data, - policyVehicleId: this.selectedPolicyVehicle?.id, - vin: value - }); - } - } catch (e) { - this.mainStore.resetVehicleState(); + // get vehicle details from selected VIN + try { + const vehicle = await this.lookupVehicleByVin(value); + if (!vehicle?.data.canSafeliteService) { + this.displayNoServiceAlert = true; + return; } + if (vehicle) { + // save selected vehicle to the store + useMainStore().updateVehicle({ + ...vehicle.data, + policyVehicleId: this.selectedPolicyVehicle?.id, + vin: value + }); + } + } catch (e) { + this.mainStore.resetVehicleState(); } } }, @@ -190,29 +190,16 @@ export default { ); }, async forwardButtonAction() { - if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + useMainStore().updateCoverageType(coverageType.NONE); + useMainStore().updateCoverageStatus(coverageStatuses.NO_COVERAGE); + this.navigateForward(); + return; + } + + const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin); + try { const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); - const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin); - - if (vehicleLookupResponse.error) { - if (vehicleLookupResponse.status === 404) { - this.mainStore.resetVehicleState(); - useMainStore().updateVehicle({ - policyVehicleId: vehicle.id, - carId: '0', - category: '', - year: vehicle.vehicleYear || '', - make: vehicle.vehicleMake || '', - model: vehicle.vehicleModel || '', - style: vehicle.vehicleStyle || '', - vin: vehicle.vin - }); - this.policyVinFound = false; - return this.navigateForward(); - } - - return; - } useMainStore().updateVehicle({ ...vehicleLookupResponse.data, @@ -225,11 +212,28 @@ export default { repairWaived: this.repairWaivedForSelectedVehicle, endorsements: this.endorsementsForSelectedVehicle }); - } else { - useMainStore().updateCoverageType(coverageType.NONE); - useMainStore().updateCoverageStatus(coverageStatuses.NO_COVERAGE); + this.navigateForward(); + } catch (e) { + debugger; + if (e.isAxiosError && e.status === 404) { + this.mainStore.resetVehicleState(); + useMainStore().updateVehicle({ + policyVehicleId: vehicle.id, + carId: '0', + category: '', + year: vehicle.vehicleYear || '', + make: vehicle.vehicleMake || '', + model: vehicle.vehicleModel || '', + style: vehicle.vehicleStyle || '', + vin: vehicle.vin + }); + this.policyVinFound = false; + this.navigateForward(); + return; + } + + throw e; } - return this.navigateForward(); }, navigateForward() { if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { @@ -264,15 +268,7 @@ export default { } }, async lookupVehicleByVin(vin) { - try { - return await useMainStore().lookupVehicleByVin(vin); - } catch (responseError) { - return { - error: true, - status: responseError.status, - data: responseError.data - }; - } + return useMainStore().lookupVehicleByVin(vin); }, async addAnotherVehicle() { this.selectedVehicleVin = vehicleSelectionOptions.VEHICLE_NOT_LISTED; diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue index e75d2c37..15e96929 100644 --- a/src/layouts/welcome-page/welcome-page.vue +++ b/src/layouts/welcome-page/welcome-page.vue @@ -328,8 +328,7 @@ export default { } promises.push(this.mainStore.getCoveragePolicyInfo()); await Promise.all(promises); - await saveSession({shouldAwaitSaveSessionQueue: true}) - .catch((error) => this.mainStore.setBailout(bailoutMessage.saveSessionError(error))); + await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true }) this.navigateForward(); }, async configureZip() { diff --git a/src/router/router.spec.js b/src/router/router.spec.js index 675fa7fa..e9a4bd6c 100644 --- a/src/router/router.spec.js +++ b/src/router/router.spec.js @@ -48,6 +48,6 @@ describe('Router', () => { // Assert expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.BAILOUT_PAGE); - expect(useMainStore().isBailout).toBeTruthy() + expect(useMainStore().isBailout).toBeTruthy(); }); }); diff --git a/src/store/index.js b/src/store/index.js index 7bcd88ab..862206e8 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -541,7 +541,7 @@ export const useMainStore = defineStore({ if (!issConfig.isCoverageEnabled || applicationUser.coverageLookupAttempts > 10) { this.updateCoverageType(coverageType.NONE); - return Promise.resolve(); + return; } this.applicationUser.coverageAttempts += 1; @@ -1198,9 +1198,6 @@ export const useMainStore = defineStore({ IsOEMRequest: this.hasOemEndorsement } } - }).catch((error) => { - console.error(error); - throw error; }); const { lineItems, serverData, isItac, primaryBillToNumber, partsWerePriced, isItacOptimized } = response.data; @@ -2442,7 +2439,7 @@ export const useMainStore = defineStore({ populateInitialState(forceReset) { if (!sessionStorage.getItem(storeId) || forceReset) { this.$state = getDefaultState(); - sessionStorage.removeItem(webStorageConstants.SUBMITTED_ORDER); + this.resetSubmittedOrder(); } }, From ea5836b3e42d21d6635708a8ceeff769cef42e52 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Thu, 19 Feb 2026 11:13:29 -0600 Subject: [PATCH 4/5] Address comments about policy / deductible / coverage calls failing should not bailout and just switch to no coverage IsVinAddressable API call failure now returns false Improved logging for cases Fixed Vehicle Style bailout when selecting select style value --- src/global-methods.js | 5 +- .../policy-vehicles/policy-vehicles.vue | 1 - .../vehicle-selection/vehicle-selection.vue | 22 +- src/main.js | 4 +- src/store/index.js | 267 +++++++++--------- src/store/store.spec.js | 26 +- 6 files changed, 169 insertions(+), 156 deletions(-) diff --git a/src/global-methods.js b/src/global-methods.js index 7a6fcf33..7f7ad72b 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -94,10 +94,11 @@ export default { } if (error.response.status !== 404) { - global.$logger.logError(`${method}: ${endpoint}: ${error.message}`, error.response); + const errorData = { method, url, payload, error }; + global.$logger.logError(`${method}: ${endpoint}`, errorData); if (bailoutOnError && global.bailoutOnAxiosError !== undefined) { - global.bailoutOnAxiosError({ method, url, payload, error }); + global.bailoutOnAxiosError(errorData); } } return reject(error.response); diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index c3b6c8ee..9cedd58d 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -214,7 +214,6 @@ export default { }); this.navigateForward(); } catch (e) { - debugger; if (e.isAxiosError && e.status === 404) { this.mainStore.resetVehicleState(); useMainStore().updateVehicle({ diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue index 1cfb7f4d..e489fe0d 100644 --- a/src/layouts/vehicle-selection/vehicle-selection.vue +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -163,16 +163,18 @@ export default { } }, selectedStyle(value) { - this.resetAlert(); - this.mainStore.updateVehicleStyle(value); - this.mainStore.setVehicle( - this.selectedYear, - this.selectedMake, - this.selectedModel, - this.selectedStyle - ).then((result) => { - this.displayNoServiceAlert = !result.data.canSafeliteService; - }); + if (value) { + this.resetAlert(); + this.mainStore.updateVehicleStyle(value); + this.mainStore.setVehicle( + this.selectedYear, + this.selectedMake, + this.selectedModel, + this.selectedStyle + ).then((result) => { + this.displayNoServiceAlert = !result.data.canSafeliteService; + }); + } } }, mounted() { diff --git a/src/main.js b/src/main.js index 243335b9..440bcaca 100644 --- a/src/main.js +++ b/src/main.js @@ -48,7 +48,7 @@ function getPageName(vm) { // Vue Error Handling vueApp.config.errorHandler = (err, vm, info) => { const pageName = getPageName(vm); - global.$logger.logError(`Page Name - ${pageName} - ${info}: ${err.message}\n${err.stack}`); + global.$logger.logError(`[${pageName}] ${info}`, err); if (applicationConfig.BAILOUT_ON_APPLICATION_ERROR) { router.navigateBailout(bailoutMessage.applicationError(`[${pageName}] ${info}: ${err.message}\n${err.stack}`)); } @@ -56,7 +56,7 @@ vueApp.config.errorHandler = (err, vm, info) => { // Vue Router Error Handling router.onError((err) => { - global.$logger.logError(err.message, err.cause); + global.$logger.logError('Router Error:', err); if (applicationConfig.BAILOUT_ON_ROUTER_ERROR) { router.navigateBailout(bailoutMessage.routerError(`${err.message}\n${err.stack}`)); } diff --git a/src/store/index.js b/src/store/index.js index 862206e8..c36f5ae2 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -25,6 +25,7 @@ import { buildURLSearchParams, getPartNumbersListForQueryString } from '@/helper import { getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper'; import { getDateForSavedSessionTimeout } from '@/helpers/session-helper'; import issPageValues from '@/router/router-constants/issPage-values'; +import CoverageStatuses from '@/constants/coverage-statuses'; const storeId = 'main'; @@ -528,12 +529,16 @@ export const useMainStore = defineStore({ payload: {} }); }, - getIsVinbyAddressPermissible() { - return globalMethods.callHttpClient({ - method: endpoints.IsVinbyAddressPermissible.method, - endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, - payload: {} - }); + async getIsVinbyAddressPermissible() { + try { + return globalMethods.callHttpClient({ + method: endpoints.IsVinbyAddressPermissible.method, + endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, + payload: {} + }); + } catch (e) { + return false; + } }, async getCoveragePolicyInfo() { const { order, issConfig, applicationUser } = this; @@ -547,42 +552,47 @@ export const useMainStore = defineStore({ this.applicationUser.coverageAttempts += 1; console.log(`Coverage lookup attempt #${this.applicationUser.coverageAttempts}. Max attempts allowed: 10.`); - const response = await globalMethods.callHttpClient({ - method: endpoints.CoveragePolicyInfo.method, - endpoint: endpoints.CoveragePolicyInfo.url, - payload: { - accountNumber: order.parentAccountNumber?.toString(), - policyNumber: policy.policyNumber, - dateOfLoss: policy.dateOfLoss, - zipCode: policy.policyZipCode, - referralCorrelationId: order.referralCorrelationId + try { + const response = await globalMethods.callHttpClient({ + method: endpoints.CoveragePolicyInfo.method, + endpoint: endpoints.CoveragePolicyInfo.url, + payload: { + accountNumber: order.parentAccountNumber?.toString(), + policyNumber: policy.policyNumber, + dateOfLoss: policy.dateOfLoss, + zipCode: policy.policyZipCode, + referralCorrelationId: order.referralCorrelationId + }, + bailoutOnError: false + }); + + const responsePolicy = response?.data?.policies?.[0]; + if (responsePolicy) { + this.updateCoverageType(coverageType.Deductible); + const insured = responsePolicy.insureds?.[0]; + + // populate parent account number + if (response.data.accountNumber) { + order.parentAccountNumber = parseInt(response.data.accountNumber, 10); + } + + // populate policy holder details from policy lookup + order.customer.address.streetAddress = insured?.address; + order.customer.address.city = insured?.city; + order.customer.address.state = insured?.state; + order.customer.address.zipCode = insured?.zipCode?.toString(); + order.customer.firstName = insured?.firstName; + order.customer.lastName = insured?.lastName; + + // populate additional fields + order.policy.policyData = responsePolicy.policyData; + + // populate vehicles + order.policy.vehicles = responsePolicy.vehicles ?? []; + } else { + this.updateCoverageType(coverageType.NONE); } - }); - - const responsePolicy = response?.data?.policies?.[0]; - if (responsePolicy) { - this.updateCoverageType(coverageType.Deductible); - const insured = responsePolicy.insureds?.[0]; - - // populate parent account number - if (response.data.accountNumber) { - order.parentAccountNumber = parseInt(response.data.accountNumber, 10); - } - - // populate policy holder details from policy lookup - order.customer.address.streetAddress = insured?.address; - order.customer.address.city = insured?.city; - order.customer.address.state = insured?.state; - order.customer.address.zipCode = insured?.zipCode?.toString(); - order.customer.firstName = insured?.firstName; - order.customer.lastName = insured?.lastName; - - // populate additional fields - order.policy.policyData = responsePolicy.policyData; - - // populate vehicles - order.policy.vehicles = responsePolicy.vehicles ?? []; - } else { + } catch (e) { this.updateCoverageType(coverageType.NONE); } }, @@ -601,82 +611,81 @@ export const useMainStore = defineStore({ updateIsItacOptimized(isItacOptimized) { this.order.insuranceCoverage.isItacOptimized = isItacOptimized || false; }, - registerClaim() { + async registerClaim() { const nonNumberCharRegex = /[^0-9]/g; - const { order, isITAC } = this; - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ + const { isITAC } = this; + + try { + const response = await globalMethods.callHttpClient({ method: endpoints.RegisterClaim.method, endpoint: endpoints.RegisterClaim.url, payload: - { - referralCorrelationId: this.order.referralCorrelationId, - accountNumber: this.order.parentAccountNumber?.toString() ?? '', - policyData: this.order.policy.policyData, - isItac: isITAC, - insured: { - firstName: this.order.customer.firstName, - lastName: this.order.customer.lastName, - address: { - addressLine1: this.order.customer.address.streetAddress, - addressLine2: this.order.customer.address.streetAddress2, - city: this.order.customer.address.city, - state: this.order.customer.address.state, - zipCode: this.order.customer.address.zipCode, - country: 'US' // TODO set from store + { + referralCorrelationId: this.order.referralCorrelationId, + accountNumber: this.order.parentAccountNumber?.toString() ?? '', + policyData: this.order.policy.policyData, + isItac: isITAC, + insured: { + firstName: this.order.customer.firstName, + lastName: this.order.customer.lastName, + address: { + addressLine1: this.order.customer.address.streetAddress, + addressLine2: this.order.customer.address.streetAddress2, + city: this.order.customer.address.city, + state: this.order.customer.address.state, + zipCode: this.order.customer.address.zipCode, + country: 'US' // TODO set from store + }, + homePhone: { + number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? '' + } }, - homePhone: { - number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? '' + driver: { + firstName: this.order.customer.firstName, + lastName: this.order.customer.lastName + }, + caller: { + homePhone: {} + }, + policyInfo: { + policyNumber: this.order.policy.policyNumber, + safelitePolicy: { + policies: [] + }, + actualDeductible: this.currentDeductible.toString() ?? '' + }, + lossInfo: { + dateOfLoss: this.order.policy.dateOfLoss, + location: { + city: this.order.policy.damageCity, + state: this.order.policy.damageState, + country: 'US' // TODO set from store + }, + vehicle: { + id: this.order.vehicle.policyVehicleId?.toString() ?? '', + year: this.order.vehicle.year?.toString() ?? '', + make: this.order.vehicle.make, + model: this.order.vehicle.model, + vin: this.order.vehicle.vin + }, + cause: this.order.policy.damageCause, + damageDescription: this.order.policy.damageCause } }, - driver: { - firstName: this.order.customer.firstName, - lastName: this.order.customer.lastName - }, - caller: { - homePhone: {} - }, - policyInfo: { - policyNumber: this.order.policy.policyNumber, - safelitePolicy: { - policies: [] - }, - actualDeductible: this.currentDeductible.toString() ?? '' - }, - lossInfo: { - dateOfLoss: this.order.policy.dateOfLoss, - location: { - city: this.order.policy.damageCity, - state: this.order.policy.damageState, - country: 'US' // TODO set from store - }, - vehicle: { - id: this.order.vehicle.policyVehicleId?.toString() ?? '', - year: this.order.vehicle.year?.toString() ?? '', - make: this.order.vehicle.make, - model: this.order.vehicle.model, - vin: this.order.vehicle.vin - }, - cause: this.order.policy.damageCause, - damageDescription: this.order.policy.damageCause - } - } - }).then((response) => { - this.order.insuranceCoverage.claimNumber = response.data.claimNumber; - if (response.data.isSuccess) { - this.updateCoverageStatus(coverageStatuses.VERIFIED); - } else { - this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); - } - return resolve(response); - }, (error) => { - this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); - this.order.insuranceCoverage.claimNumber = null; - return reject(error); + bailoutOnError: false }); - }); + this.order.insuranceCoverage.claimNumber = response.data.claimNumber; + if (response.data.isSuccess) { + this.updateCoverageStatus(coverageStatuses.VERIFIED); + } else { + this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); + } + } catch (e) { + this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); + this.order.insuranceCoverage.claimNumber = null; + } }, - getFinalDeductible() { + async getFinalDeductible() { const endorsementAnswersForPayload = []; const endorsementAnswers = this.order.policy.endorsementQuestionAnswers; if (endorsementAnswers) { @@ -692,9 +701,9 @@ export const useMainStore = defineStore({ glassInformation?.forEach((glassPiece) => { manualGlassNamesArray.push(glassPiece?.glassLocation?.toUpperCase()); }); - - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ + + try { + const r = await globalMethods.callHttpClient({ method: endpoints.FinalDeductible.method, endpoint: endpoints.FinalDeductible.url, payload: { @@ -716,16 +725,18 @@ export const useMainStore = defineStore({ vehicleVin: this.order.vehicle.vin, policyData: this.order.policy.policyData, isItac: this.isITAC - } - }).then((r) => { - this.order.policy.policyData = r.data.policyData; - this.updateDeductible(r.data); - return resolve(r); - }).catch((error) => reject(error)); - }); + }, + bailoutOnError: false + }); + this.order.policy.policyData = r.data.policyData; + this.updateDeductible(r.data); + return r; + } catch (e) { + this.updateCoverageStatus(CoverageStatuses.PENDING); + } }, - getDuplicateReferrals() { + async getDuplicateReferrals() { const params = new URLSearchParams({ parentAccountNumber: this.order.parentAccountNumber, customerPhoneNumber: this.order.contactInfo.servicePhone, @@ -734,18 +745,16 @@ export const useMainStore = defineStore({ dateOfLoss: this.order.policy.dateOfLoss }); - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ + try { + const duplicates = await globalMethods.callHttpClient({ method: endpoints.DuplicateSearch.method, - endpoint: `${endpoints.DuplicateSearch.url}?${params.toString()}` - }).then((r) => { - this.applicationUser.duplicateOrders = r.data ?? []; - return resolve(r.data); - }).catch((error) => { - this.applicationUser.duplicateOrders = []; - return reject(error); + endpoint: `${endpoints.DuplicateSearch.url}?${params.toString()}`, + bailoutOnError: false }); - }); + this.applicationUser.duplicateOrders = duplicates.data ?? []; + } catch (e) { + this.applicationUser.duplicateOrders = []; + } }, async lookupVinByPlate(licensePlate, licenseState) { try { diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 24c6139f..0e0900d2 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -459,9 +459,9 @@ describe('Store', () => { expect(store.order.insuranceCoverage.claimNumber).not.toBeNull(); }); - it('Call to client returns exception, resulting in object with error property being returned', async () => { + it('Call to client returns exception, and no error is returned from registerClaim method', async () => { // Arrange - expect.assertions(4); + expect.assertions(3); const error = 'register claim error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); store.order.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; @@ -1494,8 +1494,8 @@ describe('Store', () => { // Assert expect(globalMethods.callHttpClient).toHaveBeenCalled(); }); - it('api call throws exception => insuranceCoverage.coverageType is NONE', async () => { - expect.assertions(3); + it('api call throws exception => no error is thrown from getCoveragePolicyInfo and insuranceCoverage.coverageType is NONE', async () => { + expect.assertions(2); const error = 'get coverage policy info error'; store.issConfig.isCoverageEnabled = true; store.applicationUser.coverageLookupAttempts = 0; @@ -1567,9 +1567,9 @@ describe('Store', () => { expect(globalMethods.callHttpClient).toHaveBeenCalled(); expect(store.applicationUser.duplicateOrders).toEqual(expected); }); - it('Call to client returns exception => object with error property returned and duplicateReferrals set to []', async () => { + it('Call to client returns exception => object with error property returned and duplicateReferrals set to [] and no error is returned from getDuplicateReferrals', async () => { // Arrange - expect.assertions(3); + expect.assertions(2); const error = 'get duplicate referrals error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); @@ -1807,7 +1807,8 @@ describe('Store', () => { vehicleVin, policyData, isItac: store.isITAC - }) + }), + bailoutOnError: false })); }); it('only "Yes" endorsement answers are added to payload', () => { @@ -1912,14 +1913,15 @@ describe('Store', () => { vehicleVin, policyData, isItac: store.isITAC - }) + }), + bailoutOnError: false })); }); }); describe('unsuccessful api call', () => { - it('api call throws exception', async () => { + it('api call throws exception. no error from getFinalDeductible', async () => { // Arrange - expect.assertions(2); + expect.assertions(1); const error = 'final deductible error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); @@ -2078,8 +2080,8 @@ describe('Store', () => { expect(result.data.shopProviders[1]).toBe(provider2); }); }); - it('api call throws exception => coverageType none', async () => { - expect.assertions(3); + it('api call throws exception => coverageType none and no error from getCoveragePolicyInfo', async () => { + expect.assertions(2); const error = 'get coverage policy info error'; store.issConfig.isCoverageEnabled = true; From 86e5ac4630156082203cd6709085c03ed6232745 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Thu, 19 Feb 2026 12:34:50 -0600 Subject: [PATCH 5/5] If we have an error on entry page parsing the client or validating the client tag we should show unauthorized and log the error. --- src/layouts/entry-page/entry-page.vue | 67 +++++++++++++++------------ 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue index 9893f024..47031709 100644 --- a/src/layouts/entry-page/entry-page.vue +++ b/src/layouts/entry-page/entry-page.vue @@ -34,44 +34,51 @@ export default { computed: { }, async mounted() { - const queryStringParams = this.parseQueryParms(); - - const { isAuthorized, clientData, decryptedParams } = await this.validateClientTagOnEntry(queryStringParams); - - this.unauthorized = !isAuthorized; - if (!isAuthorized) { - // Remove the loading animation if client tag validation fails so users can see the Unauthorized Access message. - showIssLoadingModal(false); - return; - } - - this.populateISSConfigValues(clientData); - try { - // Check cookie - const issCookie = getISSCookie(); - if (issCookie !== null && issCookie.VehicleMake && issCookie.VehicleModel) { - const clientParentAccountNumber = clientData.parentAccountNumber; - const cookieParentAccountNumber = issCookie.ReferralParentAccountNumber; + const queryStringParams = this.parseQueryParms(); - if (clientParentAccountNumber === cookieParentAccountNumber) { - const savedSessionTimeStamp = new Date(issCookie.SavedSessionTimeoutDate); - const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp; + const { isAuthorized, clientData, decryptedParams } = await this.validateClientTagOnEntry(queryStringParams); - if (!isSavedSessionTimedOut) { - this.mainStore.issConfig.enableContinueFromCookie = true; + this.unauthorized = !isAuthorized; + if (!isAuthorized) { + // Remove the loading animation if client tag validation fails so users can see the Unauthorized Access message. + showIssLoadingModal(false); + return; + } + + this.populateISSConfigValues(clientData); + + try { + // Check cookie + const issCookie = getISSCookie(); + if (issCookie !== null && issCookie.VehicleMake && issCookie.VehicleModel) { + const clientParentAccountNumber = clientData.parentAccountNumber; + const cookieParentAccountNumber = issCookie.ReferralParentAccountNumber; + + if (clientParentAccountNumber === cookieParentAccountNumber) { + const savedSessionTimeStamp = new Date(issCookie.SavedSessionTimeoutDate); + const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp; + + if (!isSavedSessionTimedOut) { + this.mainStore.issConfig.enableContinueFromCookie = true; + } } + } else { + updateOrCreateISSCookie(true); } - } else { + } catch { updateOrCreateISSCookie(true); } - } catch { - updateOrCreateISSCookie(true); - } - if (clientData.parameters?.length > 0) { - const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams }); - this.populateStoreItemsFromParams(finalParams); + if (clientData.parameters?.length > 0) { + const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams }); + this.populateStoreItemsFromParams(finalParams); + } + } catch (e) { + global.$logger.logError('[Entry Page] Client Setup Error:', e); + this.unauthorized = true; + showIssLoadingModal(false); + return; } this.mainStore.applicationUser.coverageAttempts = 0;