From f3ae5d4996dd8316a81cdc95a3cffd0bea89f142 Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Tue, 9 Jan 2024 13:58:37 -0600 Subject: [PATCH 1/9] SSR-586 - Save Bailout & Callback --- src/constants/bailoutCode.js | 12 + src/helpers/bailout-helper.js | 21 ++ .../__snapshots__/bailout-page.spec.js.snap | 2 +- src/layouts/bailout-page/bailout-page.spec.js | 280 ++++++++---------- src/layouts/bailout-page/bailout-page.vue | 71 +++-- .../coverage-statement/coverage-statement.vue | 17 +- .../policy-vehicles/policy-vehicles.spec.js | 19 +- .../policy-vehicles/policy-vehicles.vue | 13 +- .../provider-preference.vue | 2 + src/layouts/tpa-search/tpa-search.vue | 7 +- src/layouts/vin-lookup/vin-lookup.vue | 5 + src/router/index.js | 13 + src/router/router-constants/router-params.js | 3 +- src/store/index.js | 38 +++ 14 files changed, 293 insertions(+), 210 deletions(-) create mode 100644 src/constants/bailoutCode.js create mode 100644 src/helpers/bailout-helper.js diff --git a/src/constants/bailoutCode.js b/src/constants/bailoutCode.js new file mode 100644 index 00000000..5ec4d476 --- /dev/null +++ b/src/constants/bailoutCode.js @@ -0,0 +1,12 @@ +const bailoutCode = Object.freeze({ + Unknown: 0, + SaveSessionError: 1, + VehicleNotFound: 2, + VehicleLookupError: 3, + CoverageStatementInvalidState: 4, + DoNotSeeMyShop: 5, + PricingResponseError: 6, + TPANotEnabled: 7 +}); + +export default bailoutCode; diff --git a/src/helpers/bailout-helper.js b/src/helpers/bailout-helper.js new file mode 100644 index 00000000..7c91ab1e --- /dev/null +++ b/src/helpers/bailout-helper.js @@ -0,0 +1,21 @@ +import { useMainStore } from '@/store'; +import BailoutCode from '@/constants/bailoutCode'; + +function canBailoutNavigateBack() { + const code = useMainStore().order.bailout.bailoutCode; + if (code == null) { + return true; + } + + switch (code) { + case BailoutCode.VehicleNotFound: + case BailoutCode.DoNotSeeMyShop: + case BailoutCode.TPANotEnabled: + return false; + + default: + return false; + } +} + +export default canBailoutNavigateBack; diff --git a/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap b/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap index c2066e76..9ad73c9b 100644 --- a/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap +++ b/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap @@ -2,13 +2,13 @@ exports[`Bailout page returns the initial data 1`] = ` Object { + "bailoutCode": null, "bailoutPageModel": Object { "email": "alexander.hamilton45@gmail.com", "firstName": "Alexander", "lastName": "Hamilton", "phoneNumber": "6145550909", }, - "notSeeingPreferredShop": false, "rules": Object { "email": "email-required|email-address-format", "firstName": "first-name-required", diff --git a/src/layouts/bailout-page/bailout-page.spec.js b/src/layouts/bailout-page/bailout-page.spec.js index 0131cc8f..8c9477fa 100644 --- a/src/layouts/bailout-page/bailout-page.spec.js +++ b/src/layouts/bailout-page/bailout-page.spec.js @@ -9,6 +9,7 @@ import routerParams from '@/router/router-constants/router-params'; import { useMainStore } from '@/store'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import settleAllPromises from '@/helpers/layout-helper.js'; +import BailoutCode from '@/constants/bailoutCode'; // Mock fetchCmsContentForPage jest.mock('@/helpers/cms-content-helper', () => ({ @@ -146,30 +147,35 @@ describe('Bailout page', () => { }); describe('computed', () => { describe('subHeaderCmsWidgetName', () => { - test.each([true, false])( - 'returns noTpa widget name when tpa flow not enabled', - (notSeeingPreferredShop) => { - // Arrange - const mainInitialState = { - issConfig: { enableTPAFlow: false } - }; - const initialData = { notSeeingPreferredShop }; - const { wrapper } = getMountedComponent(mainInitialState, initialData); - const expected = 'ContentGroupNoTPAWidget'; - - // Act - const name = wrapper.vm.subHeaderCmsWidgetName; - - // Assert - expect(name).toBe(expected); - } - ); - test('returns notSeeingPreferredShop widget name when tpa enabled and notSeeingPreferredShop true', () => { + test('returns noTpa widget name when bailout code is BailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.TPANotEnabled + } + } }; - const initialData = { notSeeingPreferredShop: true }; + const initialData = { }; + const { wrapper } = getMountedComponent(mainInitialState, initialData); + const expected = 'ContentGroupNoTPAWidget'; + + // Act + const name = wrapper.vm.subHeaderCmsWidgetName; + + // Assert + expect(name).toBe(expected); + }); + test('returns notSeeingPreferredShop widget name when bailout code is BailoutCode.DoNotSeeMyShop', () => { + // Arrange + const mainInitialState = { + order: { + bailout: { + bailoutCode: BailoutCode.DoNotSeeMyShop + } + } + }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = 'ContentGroupNotSeeingPreferredShop'; @@ -179,12 +185,16 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns default widget name when tpa enabled and notSeeingPreferredShop false', () => { + test('returns default widget name when BailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.Unknown + } + } }; - const initialData = { notSeeingPreferredShop: false }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = 'SiteSubHeaderWidget'; @@ -196,12 +206,16 @@ describe('Bailout page', () => { }); }); describe('subHeaderContentProperty', () => { - test('returns "SubHeaderText" when tpa flow enabled and not seeing preferred shop flag false', () => { + test('returns "SubHeaderText" when BailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.Unknown + } + } }; - const initialData = { notSeeingPreferredShop: false }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = 'SubHeaderText'; @@ -211,30 +225,35 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test.each([true, false])( - 'returns "HeaderText" when tpa flow not enabled', - (notSeeingPreferredShop) => { - // Arrange - const mainInitialState = { - issConfig: { enableTPAFlow: false } - }; - const initialData = { notSeeingPreferredShop }; - const { wrapper } = getMountedComponent(mainInitialState, initialData); - const expected = 'HeaderText'; - - // Act - const name = wrapper.vm.subHeaderContentProperty; - - // Assert - expect(name).toBe(expected); - } - ); - test('returns "HeaderText" when tpa flow enabled and not seeing preferred shop flag true', () => { + test('returns "HeaderText" when BailoutCode is BailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: false } + order: { + bailout: { + bailoutCode: BailoutCode.TPANotEnabled + } + } }; - const initialData = { notSeeingPreferredShop: true }; + const initialData = { }; + const { wrapper } = getMountedComponent(mainInitialState, initialData); + const expected = 'HeaderText'; + + // Act + const name = wrapper.vm.subHeaderContentProperty; + + // Assert + expect(name).toBe(expected); + }); + test('returns "HeaderText" when BailoutCode is BailoutCode.DoNotSeeMyShop', () => { + // Arrange + const mainInitialState = { + order: { + bailout: { + bailoutCode: BailoutCode.DoNotSeeMyShop + } + } + }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = 'HeaderText'; @@ -246,12 +265,16 @@ describe('Bailout page', () => { }); }); describe('subContentProperty', () => { - test('returns "SecondaryText" when tpa flow enabled and not seeing preferred shop flag false', () => { + test('returns "SecondaryText" when BailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.Unknown + } + } }; - const initialData = { notSeeingPreferredShop: false }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = 'SecondaryText'; @@ -261,30 +284,35 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test.each([true, false])( - 'returns "BodyText" when tpa flow not enabled', - (notSeeingPreferredShop) => { - // Arrange - const mainInitialState = { - issConfig: { enableTPAFlow: false } - }; - const initialData = { notSeeingPreferredShop }; - const { wrapper } = getMountedComponent(mainInitialState, initialData); - const expected = 'BodyText'; - - // Act - const name = wrapper.vm.subContentProperty; - - // Assert - expect(name).toBe(expected); - } - ); - test('returns "BodyText" when tpa flow enabled and not seeing preferred shop flag true', () => { + test('returns "BodyText" when BailoutCode is BailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.TPANotEnabled + } + } }; - const initialData = { notSeeingPreferredShop: true }; + const initialData = { }; + const { wrapper } = getMountedComponent(mainInitialState, initialData); + const expected = 'BodyText'; + + // Act + const name = wrapper.vm.subContentProperty; + + // Assert + expect(name).toBe(expected); + }); + test('returns "BodyText" when BailoutCode is BailoutCode.DoNotSeeMyShop', () => { + // Arrange + const mainInitialState = { + order: { + bailout: { + bailoutCode: BailoutCode.DoNotSeeMyShop + } + } + }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = 'BodyText'; @@ -296,12 +324,16 @@ describe('Bailout page', () => { }); }); describe('stripRteStyle', () => { - test('returns false when tpa flow enabled and not seeing preferred shop flag false', () => { + test('returns false when BailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.Unknown + } + } }; - const initialData = { notSeeingPreferredShop: false }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = false; @@ -311,12 +343,16 @@ describe('Bailout page', () => { // Assert expect(flag).toBe(expected); }); - test('returns true when tpa flow not enabled and not seeing preferred shop flag false', () => { + test('returns true when BailoutCode is BailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: false } + order: { + bailout: { + bailoutCode: BailoutCode.TPANotEnabled + } + } }; - const initialData = { notSeeingPreferredShop: false }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = true; @@ -326,12 +362,16 @@ describe('Bailout page', () => { // Assert expect(flag).toBe(expected); }); - test('returns true when tpa flow enabled and not seeing preferred shop flag true', () => { + test('returns true when when BailoutCode is BailoutCode.DoNotSeeMyShop', () => { // Arrange const mainInitialState = { - issConfig: { enableTPAFlow: true } + order: { + bailout: { + bailoutCode: BailoutCode.DoNotSeeMyShop + } + } }; - const initialData = { notSeeingPreferredShop: true }; + const initialData = { }; const { wrapper } = getMountedComponent(mainInitialState, initialData); const expected = true; @@ -342,24 +382,6 @@ describe('Bailout page', () => { expect(flag).toBe(expected); }); }); - describe('isTpaEnabled', () => { - test.each([true, false])( - 'matches enableTPAFlow in store', - (enableTPAFlow) => { - // Arrange - const mainInitialState = { - issConfig: { enableTPAFlow } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Act - const flag = wrapper.vm.isTpaEnabled; - - // Assert - expect(flag).toBe(enableTPAFlow); - } - ); - }); }); describe('method', () => { describe('backButtonAction', () => { @@ -390,8 +412,7 @@ describe('Bailout page', () => { wrapper.vm.navigationScenarios.CLICKED_FORWARD, wrapper.vm.$route, {}, - {}, - wrapper.vm.bailoutPageModel + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); }); }); @@ -426,62 +447,5 @@ describe('Bailout page', () => { }); }); }); - describe('setNotSeeingPreferShop', () => { - test.each([true, false])( - 'updates notSeeingPreferredShop value', - (flag) => { - // Arrange - const { wrapper } = getMountedComponent(); - - // Act - wrapper.vm.setNotSeeingPreferShop(flag); - - // Assert - expect(wrapper.vm.notSeeingPreferredShop).toBe(flag); - } - ); - test('sets notSeeingPreferredShop to true when null is passed', () => { - // Arrange - const { wrapper } = getMountedComponent(); - - // Act - wrapper.vm.setNotSeeingPreferShop(null); - - // Assert - expect(wrapper.vm.notSeeingPreferredShop).toBe(false); - }); - }); - }); - - describe('before entering the route', () => { - test.each([true, false])( - 'sets notSeeingPreferredShop flag based value returned by store method pageData', - async (notSeeingPreferredShop) => { - // Arrange - const page = 'bailout-page'; - const initialStoreState = { - applicationUser: { - pageData: { - [page]: { - [routerParams.NOT_SEEING_PREFERRED_SHOP]: notSeeingPreferredShop, - turtle: 5 - } - } - } - }; - const { wrapper } = getMountedComponent(initialStoreState); - - // Act - await bailoutPage.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: page } }, - undefined, - (c) => c(wrapper.vm) - ); - - // Assert - expect(wrapper.vm.notSeeingPreferredShop).toBe(notSeeingPreferredShop); - } - ); }); }); diff --git a/src/layouts/bailout-page/bailout-page.vue b/src/layouts/bailout-page/bailout-page.vue index 59ba9bf4..0e41ec66 100644 --- a/src/layouts/bailout-page/bailout-page.vue +++ b/src/layouts/bailout-page/bailout-page.vue @@ -55,6 +55,7 @@ class="footer-content-container" cmsWidgetName="SiteFooterWidget" :isForwardActionDisabled="!meta.valid" + :isBackButtonHidden="!canNavigateBack" @backClicked="backButtonAction" @ForwardClicked="forwardButtonAction" /> @@ -76,6 +77,8 @@ import settleAllPromises from '@/helpers/layout-helper'; import { useMainStore } from '@/store'; import widgetFields from '@/constants/cms-widget-fields.js'; import routerParams from '@/router/router-constants/router-params'; +import canNavigateBackFromBailout from '@/helpers/bailout-helper'; +import BailoutCode from '@/constants/bailoutCode'; export default { name: 'bailout-page', @@ -101,18 +104,18 @@ export default { // use resultMap to populate layout content. const resultMap = await settleAllPromises(promiseResultMap); - const pageData = useMainStore().pageData(to.query.issPage); next((vm) => { vm.setCmsContent(resultMap.cmsContent); - if (pageData && pageData[routerParams.NOT_SEEING_PREFERRED_SHOP]) { - vm.setNotSeeingPreferShop(pageData[routerParams.NOT_SEEING_PREFERRED_SHOP]); - } }); }, + setup() { + const mainStore = useMainStore(); + return { mainStore }; + }, data() { return { - notSeeingPreferredShop: false, bailoutPageModel: this.getBailoutPageModelFromStore(), + bailoutCode: this.mainStore.order.bailout.bailoutCode, widget: { defaultSiteHeader: 'SiteSubHeaderWidget', noTpa: 'ContentGroupNoTPAWidget', @@ -128,43 +131,64 @@ export default { }, computed: { subHeaderCmsWidgetName() { - if (!this.isTpaEnabled) { - return this.widget.noTpa; + switch (this.bailoutCode) { + case BailoutCode.TPANotEnabled: + return this.widget.noTpa; + + case BailoutCode.DoNotSeeMyShop: + return this.widget.notSeeingPreferredShop; + + default: + return this.widget.defaultSiteHeader; } - if (this.notSeeingPreferredShop) { - return this.widget.notSeeingPreferredShop; - } - return this.widget.defaultSiteHeader; }, subHeaderContentProperty() { - return !this.isTpaEnabled || this.notSeeingPreferredShop - ? widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT - : widgetFields.SUB_HEADER_WIDGET.SUB_HEADER_TEXT; + switch (this.bailoutCode) { + case BailoutCode.TPANotEnabled: + case BailoutCode.DoNotSeeMyShop: + return widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT; + + default: + return widgetFields.SUB_HEADER_WIDGET.SUB_HEADER_TEXT; + } }, subContentProperty() { - return !this.isTpaEnabled || this.notSeeingPreferredShop - ? widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT - : widgetFields.SUB_HEADER_WIDGET.SECONDARY_TEXT; + switch (this.bailoutCode) { + case BailoutCode.TPANotEnabled: + case BailoutCode.DoNotSeeMyShop: + return widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT; + + default: + return widgetFields.SUB_HEADER_WIDGET.SECONDARY_TEXT; + } }, stripRteStyle() { - return !this.isTpaEnabled || this.notSeeingPreferredShop; + switch (this.bailoutCode) { + case BailoutCode.TPANotEnabled: + case BailoutCode.DoNotSeeMyShop: + return true; + + default: + return false; + } }, - isTpaEnabled() { - return useMainStore().issConfig.enableTPAFlow; + canNavigateBack() { + return canNavigateBackFromBailout(); } }, methods: { backButtonAction() { // route to move backwards + this.mainStore.resetBailout(); this.$router.navigate(this.navigationScenarios.CLICKED_BACK_PREVIOUS, this.$route); }, forwardButtonAction() { + this.mainStore.setBailoutContactInfo(this.bailoutPageModel); this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD, this.$route, {}, - {}, - this.bailoutPageModel + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); }, getBailoutPageModelFromStore() { @@ -174,9 +198,6 @@ export default { phoneNumber: useMainStore().order.customer.phoneNumber, email: useMainStore().order.customer.emailAddress }; - }, - setNotSeeingPreferShop(value) { - this.notSeeingPreferredShop = value ?? false; } } }; diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index fa6c419a..d6828009 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -128,6 +128,7 @@ import baseFormMixin from '@/mixins/base-form-mixin.js'; import navigationScenarios from '@/router/router-constants/navigation-scenarios.js'; import routerParams from '@/router/router-constants/router-params'; import issPageValues from '@/router/router-constants/issPage-values'; +import bailoutCode from '@/constants/bailoutCode'; export default { name: 'coverage-statement', @@ -175,17 +176,9 @@ export default { await useMainStore().getFinalDeductible(); pricingResults = await useMainStore().getPriceOrderItems(availableLineItems) .catch((err) => { - const errorPageData = { - errorData: err.data, - functionLocation: 'beforeRouteEnter', - functionName: 'getPriceOrderItems', - functionParameters: { - availableLineItems - }, - routeFrom: from, - routeTo: to - }; - useMainStore().updatePageData({ page: issPageValues.BAILOUT_PAGE, data: errorPageData }); + this.mainStore.setBailout(this.$router, bailoutCode.PricingResponseError, 'An error occurred in getPriceOrderItems.' + + ` ${availableLineItems.join(', ')}` + + ` ${err.data}`); next(`/?issPage=${issPageValues.BAILOUT_PAGE}`); }); } @@ -394,6 +387,7 @@ export default { { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } else { + this.mainStore.setBailout(this.$router, bailoutCode.TPANotEnabled, 'User selected TPA when TPA is not enabled for this client'); this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, this.$route, @@ -402,6 +396,7 @@ export default { ); } } else { + this.mainStore.setBailout(this.$router, bailoutCode.CoverageStatementInvalidState, 'Coverage Statement has entered an invalid state'); this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE, this.$route, diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index 9f9ca75e..eb1225a8 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -10,6 +10,7 @@ import { getRandomString, getRandomInt } from '@/helpers/data-generation'; import endorsementOptions from '@/constants/endorsement-options'; import { createTestingPinia } from '@pinia/testing'; import vehicleSelectionOptions from '@/constants/vehicle-selection-options'; +import bailoutCode from "@/constants/bailoutCode"; // Mock fetchCmsContentForPage jest.mock('@/helpers/cms-content-helper', () => ({ @@ -73,6 +74,10 @@ function setupMocks() { return { wrapper }; } +beforeEach(() => { + useMainStore().order.bailout.bailoutCode = null; +}); + describe('policy-vehicles.vue', () => { test('Should navigate to CLICKED_BACK if backButtonAction is run', async () => { // Arrange @@ -100,7 +105,6 @@ describe('policy-vehicles.vue', () => { policyVehicles: [ { vin } ], - bailout: false, policyVinFound: true }); @@ -241,19 +245,25 @@ describe('policy-vehicles.vue', () => { async () => { // Arrange const { wrapper } = setupMocks({}); - wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 500 }); + const lookupReturnValue = { error: true, status: 500, data: 'error' }; + wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(lookupReturnValue); const vin = getRandomString(17, 17); await wrapper.setData({ selectedVehicleVin: vin, - bailout: false + policyVehicles: [ + { + vin + } + ] }); // Act + wrapper.vm.mainStore.order.bailout.bailoutCode = bailoutCode.VehicleLookupError; await wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.bailout).toBeTruthy(); + expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(wrapper.vm.$router, bailoutCode.VehicleLookupError, `An error occurred looking up Vin: ${vin}. Error: ${lookupReturnValue.data}`); expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, undefined, @@ -274,7 +284,6 @@ describe('policy-vehicles.vue', () => { const vin = getRandomString(17, 17); await wrapper.setData({ selectedVehicleVin: vin, - bailout: false, policyVinFound: true, policyVehicles: [{ vin, diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index c2a0741b..c9e6ba9b 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -53,6 +53,7 @@ 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 bailoutCode from '@/constants/bailoutCode'; export default { name: 'policy-vehicles', @@ -71,6 +72,10 @@ export default { vm.setCmsContent(cmsContentPromise); }); }, + setup() { + const mainStore = useMainStore(); + return { mainStore }; + }, data() { const policyVehicles = useMainStore().order.policy.vehicles; return { @@ -78,7 +83,6 @@ export default { selectedVehicleVin: '', displayGeneric: true, policyVinFound: true, - bailout: false, rules: { optionRequired: globalRules.OPTION_REQUIRED } @@ -197,7 +201,7 @@ export default { return this.navigateForward(); } - this.bailout = true; + this.mainStore.setBailout(this.$router, bailoutCode.VehicleLookupError, `An error occurred looking up Vin: ${vehicle.vin}. Error: ${vehicleLookupResponse.data}`); return this.navigateForward(); } @@ -215,7 +219,7 @@ export default { return this.navigateForward(); }, navigateForward() { - if (this.bailout) { + if (this.mainStore.isBailout) { this.$router .navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, @@ -260,7 +264,8 @@ export default { } catch (responseError) { return { error: true, - status: responseError.status + status: responseError.status, + data: responseError.data }; } } diff --git a/src/layouts/provider-preference/provider-preference.vue b/src/layouts/provider-preference/provider-preference.vue index b5d76d26..1b15a05b 100644 --- a/src/layouts/provider-preference/provider-preference.vue +++ b/src/layouts/provider-preference/provider-preference.vue @@ -68,6 +68,7 @@ import steeringModal from '@/layouts/provider-preference/steering-modal/steering import shopPreferenceModal from '@/layouts/provider-preference/shop-preference-modal/shop-preference-modal.vue'; import tpaRecalModal from '@/layouts/provider-preference/tpa-recal-modal/tpa-recal-modal.vue'; import globalRules from '@/constants/global-rules'; +import bailoutCode from '@/constants/bailoutCode'; const options = { SAFELITE: 'SafeliteOption', TPA: 'TPAOption' }; @@ -197,6 +198,7 @@ export default { } scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED; } else { + this.mainStore.setBailout(this.$router, bailoutCode.TPANotEnabled, 'User selected TPA when TPA is not enabled for this client'); scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED; } break; diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 8ec7742e..f0312e43 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -134,6 +134,7 @@ import widgetFields from '@/constants/cms-widget-fields.js'; import { shallowRef } from 'vue'; import routerParams from '@/router/router-constants/router-params'; import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js'; +import bailoutCode from '@/constants/bailoutCode'; const radiusFilterPairs = [ { radius: 15, filter: '15 miles' }, @@ -325,12 +326,10 @@ export default { return getTpaProvidersResult?.data?.shopProviders ?? []; }, doNotSeeMyShopLinkClick() { + this.mainStore.setBailout(this.$router, bailoutCode.DoNotSeeMyShop, 'User does not see their shop.'); this.$router.navigate( this.navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK, - this.$route, - {}, - {}, - { [routerParams.NOT_SEEING_PREFERRED_SHOP]: true } + this.$route ); }, async searchClick() { diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index f9e9fb9a..f8df2f51 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -63,6 +63,7 @@ import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vinLocationInformation from '@/layouts/vin-lookup/vin-location-information/vin-location-information.vue'; import vinLookupAlerts from '@/layouts/vin-lookup/vin-lookup-alerts/vin-lookup-alerts.vue'; import vinQuestion from '@/layouts/vin-lookup/vin-question/vin-question.vue'; +import bailoutCode from '@/constants/bailoutCode'; export default { name: 'vin-lookup', @@ -183,14 +184,18 @@ export default { if (vehicleLookupResponse.error) { this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; + this.mainStore.setBailout(this.$router, bailoutCode.VehicleNotFound, `Failed to find vehicle in system with vin: ${this.vin}`); this.resetVehicleFromLookup(); this.$refs.siteFooter.removeLoader(); // 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(); + return; } + this.mainStore.resetBailout(); + // Add vin bcs the response from the service doesn't contain vin this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { vin: this.vin }); } diff --git a/src/router/index.js b/src/router/index.js index 78fe7005..caf0c784 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -15,7 +15,10 @@ import applicationConfig from '@/constants/application-config'; import analyticsMixin from '@/mixins/analytics-mixin'; import { saveSession } from '@/helpers/order-helper.js'; import routerParams from '@/router/router-constants/router-params'; +import bailoutCode from '@/constants/bailoutCode'; +import IssPageValues from '@/router/router-constants/issPage-values'; import navigationScenarios from './router-constants/navigation-scenarios'; +import canBailoutNavigateBack from "@/helpers/bailout-helper"; const routes = [ { @@ -118,6 +121,15 @@ const router = createRouter({ } }); +router.beforeEach(async (to, from) => { + const store = useMainStore(); + // Prevent navigating backwards if we enter a bailout that we are not allowed to go back on + if (store.isBailout && from.name === IssPageValues.BAILOUT_PAGE && to.name !== IssPageValues.CONTACT_CONFIRMATION + && !canBailoutNavigateBack()) { + return false; + } +}); + router.afterEach(async (to, from) => { /*eslint-disable-line*/ const store = useMainStore(); @@ -131,6 +143,7 @@ router.afterEach(async (to, from) => { const saveSessionSynchronous = !!from.params[routerParams.SAVE_SESSION_SYNCHRONOUS]; await saveSession({ shouldAwaitSaveSessionQueue: saveSessionSynchronous }).catch((error) => { if (from.name === issPageValues.WELCOME_PAGE) { + store.setBailout(router, bailoutCode.SaveSessionError, error.data); router.navigate( navigationScenarios.SAVE_SESSION_FAILED, { query: { issPage: issPageValues.WELCOME_PAGE } } diff --git a/src/router/router-constants/router-params.js b/src/router/router-constants/router-params.js index e5758bf4..2465f965 100644 --- a/src/router/router-constants/router-params.js +++ b/src/router/router-constants/router-params.js @@ -1,7 +1,6 @@ const routerParams = Object.freeze({ DISPLAY_VEHICLE_CHANGE_ALERT: 'displayVehicleChangeAlert', - SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous', - NOT_SEEING_PREFERRED_SHOP: 'notSeeingPreferredShop' + SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous' }); export default routerParams; diff --git a/src/store/index.js b/src/store/index.js index 803fb30f..4cfd45e0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -163,6 +163,13 @@ const getDefaultState = () => ({ jobMaxMinutes: null, jobMinMinutes: null }, + bailout: { + page: null, + url: null, + bailoutCode: null, + errorMessage: null, + submit: false + }, referralNumber: null, referralDate: null, referralCorrelationId: '00000000-0000-0000-0000-000000000000', @@ -221,6 +228,7 @@ export const useMainStore = defineStore({ || state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP, isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF, isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, + isBailout: (state) => state.order.bailout.bailoutCode !== null, eventBusItem: (state) => (eventCategory, eventSubCategory) => { const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory); return matchedEvent?.eventValue; @@ -1148,6 +1156,12 @@ export const useMainStore = defineStore({ jobMaxMinutes: schedule.jobMaxMinutes, jobMinMinutes: schedule.jobMinMinutes }, + bailout: this.order.bailout.submit ? { + page: this.order.bailout.page, + url: this.order.bailout.url, + bailoutCode: this.order.bailout.bailoutCode, + errorMessage: this.order.bailout.errorMessage + } : null, referralDate: this.order.referralDate, referralNumber: this.order.referralNumber?.toString(), referralCorrelationId: this.order.referralCorrelationId, @@ -1404,6 +1418,14 @@ export const useMainStore = defineStore({ this.order.serviceLocation.isVehicleProtected = null; }, + resetBailout() { + this.order.bailout.url = null; + this.order.bailout.page = null; + this.order.bailout.bailoutCode = null; + this.order.bailout.errorMessage = null; + this.order.bailout.submit = false; + }, + updateSupportingItems(partsData) { this.order.lineItems.supportingItems = partsData; }, @@ -2047,6 +2069,21 @@ export const useMainStore = defineStore({ this.updateRegistration(registrationInfo); }, + setBailout(router, code, errorMessage) { + this.order.bailout.url = window.location.href; + this.order.bailout.page = router.currentRoute.value.name; + this.order.bailout.bailoutCode = code; + this.order.bailout.errorMessage = errorMessage; + }, + + setBailoutContactInfo(contact) { + this.order.customer.firstName = contact.firstName; + this.order.customer.lastName = contact.lastName; + this.order.customer.phoneNumber = contact.phoneNumber; + this.order.customer.emailAddress = contact.email; + this.order.bailout.submit = true; + }, + resetRegistrationAndDependencies() { this.resetRegistrationState(); this.resetGlassPartsState(); @@ -2087,6 +2124,7 @@ export const useMainStore = defineStore({ this.order.customer.address.streetAddress2 = null; this.resetVehicleState(); this.resetDamageState(); + this.resetBailout(); } }, From 6dba70c156f6f0da8fec71d3f3e9c1cb06fa103e Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Tue, 9 Jan 2024 14:22:58 -0600 Subject: [PATCH 2/9] SSR-586 Merge from develop --- src/router/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/router/index.js b/src/router/index.js index 8b4f4eb9..1700a50d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -122,7 +122,7 @@ const router = createRouter({ } }); -router.beforeEach(async (to, from, next) => { +router.beforeEach(async (to, from) => { const fromQueryPage = from.query?.issPage; if (fromQueryPage === undefined) { showIssLoadingModal(true); @@ -135,7 +135,7 @@ router.beforeEach(async (to, from, next) => { return false; } - next(); + return true; }); router.afterEach(async (to, from) => { From 04c375a341115e4ead9a7c0eb44f76e3abf3012f Mon Sep 17 00:00:00 2001 From: Josh Dassinger Date: Fri, 12 Jan 2024 13:58:59 -0600 Subject: [PATCH 3/9] SSR-586 PR Changes Moved from store to pageData Added bailoutMessage const update setBailout method to accept currentPage or to/from types --- src/constants/bailoutMessage.js | 50 +++++++ src/helpers/bailout-helper.js | 5 +- .../__snapshots__/bailout-page.spec.js.snap | 4 +- src/layouts/bailout-page/bailout-page.spec.js | 131 +++++++++++------- src/layouts/bailout-page/bailout-page.vue | 11 +- .../coverage-statement/coverage-statement.vue | 31 +++-- .../policy-vehicles/policy-vehicles.spec.js | 15 +- .../policy-vehicles/policy-vehicles.vue | 3 +- .../provider-preference.vue | 3 +- .../service-packages/service-packages.vue | 30 ++-- src/layouts/tpa-search/tpa-search.vue | 3 +- src/layouts/vin-lookup/vin-lookup.vue | 3 +- src/router/index.js | 5 +- src/store/index.js | 42 +++--- 14 files changed, 213 insertions(+), 123 deletions(-) create mode 100644 src/constants/bailoutMessage.js diff --git a/src/constants/bailoutMessage.js b/src/constants/bailoutMessage.js new file mode 100644 index 00000000..f4d204b6 --- /dev/null +++ b/src/constants/bailoutMessage.js @@ -0,0 +1,50 @@ +import bailoutCode from '@/constants/bailoutCode'; + +function getItemData(data) { + if (data === undefined || data == null) { + return 'null'; + } + + if (typeof data === 'string') { + return data; + } + + return JSON.stringify(data); +} + +const bailoutMessage = Object.freeze({ + unknown: (error) => ({ + code: bailoutCode.Unknown, + message: `An unknown bailout occurred: ${getItemData(error)}` + }), + saveSessionError: (error) => ({ + code: bailoutCode.SaveSessionError, + message: `An error occurred during save session: ${getItemData(error)}` + }), + vehicleNotFound: (vin) => ({ + code: bailoutCode.VehicleNotFound, + message: `Failed to find vehicle in system with vin: ${vin}` + }), + vehicleLookupError: (vin, error) => ({ + code: bailoutCode.VehicleLookupError, + message: `An error occurred looking up Vin: ${vin}. Error: ${getItemData(error)}` + }), + coverageStatementInvalidState: () => ({ + code: bailoutCode.CoverageStatementInvalidState, + message: 'Coverage Statement has entered an invalid state' + }), + doNotSeeMyShop: () => ({ + code: bailoutCode.DoNotSeeMyShop, + message: 'User does not see their shop.' + }), + pricingResponseError: (lineItems, error) => ({ + code: bailoutCode.PricingResponseError, + message: `An error occurred in getPriceOrderItems. Line Items: ${getItemData(lineItems)} Error: ${getItemData(error)}` + }), + TPANotEnabled: () => ({ + code: bailoutCode.TPANotEnabled, + message: 'User selected TPA when TPA is not enabled for this client' + }) +}); + +export default bailoutMessage; diff --git a/src/helpers/bailout-helper.js b/src/helpers/bailout-helper.js index 7c91ab1e..e267faa8 100644 --- a/src/helpers/bailout-helper.js +++ b/src/helpers/bailout-helper.js @@ -1,8 +1,9 @@ import { useMainStore } from '@/store'; import BailoutCode from '@/constants/bailoutCode'; +import issPageValues from "@/router/router-constants/issPage-values"; function canBailoutNavigateBack() { - const code = useMainStore().order.bailout.bailoutCode; + const code = useMainStore().pageData(issPageValues.BAILOUT_PAGE)?.bailoutCode; if (code == null) { return true; } @@ -11,7 +12,7 @@ function canBailoutNavigateBack() { case BailoutCode.VehicleNotFound: case BailoutCode.DoNotSeeMyShop: case BailoutCode.TPANotEnabled: - return false; + return true; default: return false; diff --git a/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap b/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap index 9ad73c9b..62f2829b 100644 --- a/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap +++ b/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap @@ -2,7 +2,9 @@ exports[`Bailout page returns the initial data 1`] = ` Object { - "bailoutCode": null, + "bailout": Object { + "bailoutCode": 0, + }, "bailoutPageModel": Object { "email": "alexander.hamilton45@gmail.com", "firstName": "Alexander", diff --git a/src/layouts/bailout-page/bailout-page.spec.js b/src/layouts/bailout-page/bailout-page.spec.js index 8c9477fa..d68ea73d 100644 --- a/src/layouts/bailout-page/bailout-page.spec.js +++ b/src/layouts/bailout-page/bailout-page.spec.js @@ -9,7 +9,7 @@ import routerParams from '@/router/router-constants/router-params'; import { useMainStore } from '@/store'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import settleAllPromises from '@/helpers/layout-helper.js'; -import BailoutCode from '@/constants/bailoutCode'; +import bailoutCode from '@/constants/bailoutCode'; // Mock fetchCmsContentForPage jest.mock('@/helpers/cms-content-helper', () => ({ @@ -29,7 +29,16 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu const testingPinia = createTestingPinia({ initialState: { - main: mainInitialState + main: { + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.Unknown + } + } + }, + ...mainInitialState + } } }); useMainStore(testingPinia); @@ -150,9 +159,11 @@ describe('Bailout page', () => { test('returns noTpa widget name when bailout code is BailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.TPANotEnabled + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.TPANotEnabled + } } } }; @@ -166,12 +177,14 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns notSeeingPreferredShop widget name when bailout code is BailoutCode.DoNotSeeMyShop', () => { + test('returns notSeeingPreferredShop widget name when bailout code is bailoutCode.DoNotSeeMyShop', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.DoNotSeeMyShop + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.DoNotSeeMyShop + } } } }; @@ -185,12 +198,14 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns default widget name when BailoutCode does not match any other content bailout codes', () => { + test('returns default widget name when bailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.Unknown + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.Unknown + } } } }; @@ -206,12 +221,14 @@ describe('Bailout page', () => { }); }); describe('subHeaderContentProperty', () => { - test('returns "SubHeaderText" when BailoutCode does not match any other content bailout codes', () => { + test('returns "SubHeaderText" when bailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.Unknown + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.Unknown + } } } }; @@ -225,12 +242,14 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns "HeaderText" when BailoutCode is BailoutCode.TPANotEnabled', () => { + test('returns "HeaderText" when bailoutCode is bailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.TPANotEnabled + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.TPANotEnabled + } } } }; @@ -244,12 +263,14 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns "HeaderText" when BailoutCode is BailoutCode.DoNotSeeMyShop', () => { + test('returns "HeaderText" when bailoutCode is bailoutCode.DoNotSeeMyShop', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.DoNotSeeMyShop + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.DoNotSeeMyShop + } } } }; @@ -265,12 +286,14 @@ describe('Bailout page', () => { }); }); describe('subContentProperty', () => { - test('returns "SecondaryText" when BailoutCode does not match any other content bailout codes', () => { + test('returns "SecondaryText" when bailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.Unknown + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.Unknown + } } } }; @@ -284,12 +307,14 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns "BodyText" when BailoutCode is BailoutCode.TPANotEnabled', () => { + test('returns "BodyText" when bailoutCode is bailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.TPANotEnabled + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.TPANotEnabled + } } } }; @@ -303,12 +328,14 @@ describe('Bailout page', () => { // Assert expect(name).toBe(expected); }); - test('returns "BodyText" when BailoutCode is BailoutCode.DoNotSeeMyShop', () => { + test('returns "BodyText" when bailoutCode is bailoutCode.DoNotSeeMyShop', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.DoNotSeeMyShop + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.DoNotSeeMyShop + } } } }; @@ -324,12 +351,14 @@ describe('Bailout page', () => { }); }); describe('stripRteStyle', () => { - test('returns false when BailoutCode does not match any other content bailout codes', () => { + test('returns false when bailoutCode does not match any other content bailout codes', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.Unknown + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.Unknown + } } } }; @@ -343,12 +372,14 @@ describe('Bailout page', () => { // Assert expect(flag).toBe(expected); }); - test('returns true when BailoutCode is BailoutCode.TPANotEnabled', () => { + test('returns true when bailoutCode is bailoutCode.TPANotEnabled', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.TPANotEnabled + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.TPANotEnabled + } } } }; @@ -362,12 +393,14 @@ describe('Bailout page', () => { // Assert expect(flag).toBe(expected); }); - test('returns true when when BailoutCode is BailoutCode.DoNotSeeMyShop', () => { + test('returns true when when bailoutCode is bailoutCode.DoNotSeeMyShop', () => { // Arrange const mainInitialState = { - order: { - bailout: { - bailoutCode: BailoutCode.DoNotSeeMyShop + applicationUser: { + pageData: { + 'bailout-page': { + bailoutCode: bailoutCode.DoNotSeeMyShop + } } } }; diff --git a/src/layouts/bailout-page/bailout-page.vue b/src/layouts/bailout-page/bailout-page.vue index 0e41ec66..87797893 100644 --- a/src/layouts/bailout-page/bailout-page.vue +++ b/src/layouts/bailout-page/bailout-page.vue @@ -79,6 +79,7 @@ import widgetFields from '@/constants/cms-widget-fields.js'; import routerParams from '@/router/router-constants/router-params'; import canNavigateBackFromBailout from '@/helpers/bailout-helper'; import BailoutCode from '@/constants/bailoutCode'; +import issPageValues from '@/router/router-constants/issPage-values'; export default { name: 'bailout-page', @@ -115,7 +116,7 @@ export default { data() { return { bailoutPageModel: this.getBailoutPageModelFromStore(), - bailoutCode: this.mainStore.order.bailout.bailoutCode, + bailout: this.mainStore.pageData(issPageValues.BAILOUT_PAGE), widget: { defaultSiteHeader: 'SiteSubHeaderWidget', noTpa: 'ContentGroupNoTPAWidget', @@ -131,7 +132,7 @@ export default { }, computed: { subHeaderCmsWidgetName() { - switch (this.bailoutCode) { + switch (this.bailout.bailoutCode) { case BailoutCode.TPANotEnabled: return this.widget.noTpa; @@ -143,7 +144,7 @@ export default { } }, subHeaderContentProperty() { - switch (this.bailoutCode) { + switch (this.bailout.bailoutCode) { case BailoutCode.TPANotEnabled: case BailoutCode.DoNotSeeMyShop: return widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT; @@ -153,7 +154,7 @@ export default { } }, subContentProperty() { - switch (this.bailoutCode) { + switch (this.bailout.bailoutCode) { case BailoutCode.TPANotEnabled: case BailoutCode.DoNotSeeMyShop: return widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT; @@ -163,7 +164,7 @@ export default { } }, stripRteStyle() { - switch (this.bailoutCode) { + switch (this.bailout.bailoutCode) { case BailoutCode.TPANotEnabled: case BailoutCode.DoNotSeeMyShop: return true; diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 363e93f2..cacb8ce3 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -129,6 +129,7 @@ import navigationScenarios from '@/router/router-constants/navigation-scenarios. import routerParams from '@/router/router-constants/router-params'; import issPageValues from '@/router/router-constants/issPage-values'; import bailoutCode from '@/constants/bailoutCode'; +import bailoutMessage from '@/constants/bailoutMessage'; export default { name: 'coverage-statement', @@ -171,27 +172,29 @@ export default { ...(clonedGlassParts ?? []) ]; + let hasBailedOut = false; let pricingResults = []; if (useMainStore().order.policy.policyLookupSuccessful && useMainStore().order.vehicle.policyVehicleId >= 0) { await useMainStore().getFinalDeductible(); pricingResults = await useMainStore().getPriceOrderItems(availableLineItems) .catch((err) => { - this.mainStore.setBailout(this.$router, bailoutCode.PricingResponseError, 'An error occurred in getPriceOrderItems.' - + ` ${availableLineItems.join(', ')}` - + ` ${err.data}`); + useMainStore().setBailout(to, bailoutMessage.pricingResponseError(availableLineItems.map((li) => li.partNumber), { code: err.code, message: err.message, data: err.data })); + hasBailedOut = true; next(`/?issPage=${issPageValues.BAILOUT_PAGE}`); }); } - // Call the "next" function to complete the transition to this page. - next((vm) => { - vm.setCmsContent(resultMap.cmsContent); - vm.setSupportingItems(resultMap.supportingItems); - // eslint-disable-next-line no-param-reassign - vm.availableLineItems = pricingResults; - vm.$refs.loadingModal.showModal(); - vm.initializeComponent(availableLineItems); - }); + if (!hasBailedOut) { + // Call the "next" function to complete the transition to this page. + next((vm) => { + vm.setCmsContent(resultMap.cmsContent); + vm.setSupportingItems(resultMap.supportingItems); + // eslint-disable-next-line no-param-reassign + vm.availableLineItems = pricingResults; + vm.$refs.loadingModal.showModal(); + vm.initializeComponent(availableLineItems); + }); + } }, setup() { const mainStore = useMainStore(); @@ -387,7 +390,7 @@ export default { { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } else { - this.mainStore.setBailout(this.$router, bailoutCode.TPANotEnabled, 'User selected TPA when TPA is not enabled for this client'); + this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.TPANotEnabled()); this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, this.$route, @@ -396,7 +399,7 @@ export default { ); } } else { - this.mainStore.setBailout(this.$router, bailoutCode.CoverageStatementInvalidState, 'Coverage Statement has entered an invalid state'); + this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.coverageStatementInvalidState()); this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE, this.$route, diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index eb1225a8..8ffcdda7 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -10,7 +10,9 @@ import { getRandomString, getRandomInt } from '@/helpers/data-generation'; import endorsementOptions from '@/constants/endorsement-options'; import { createTestingPinia } from '@pinia/testing'; import vehicleSelectionOptions from '@/constants/vehicle-selection-options'; -import bailoutCode from "@/constants/bailoutCode"; +import bailoutCode from '@/constants/bailoutCode'; +import bailoutMessage from '@/constants/bailoutMessage'; +import issPageValues from '@/router/router-constants/issPage-values'; // Mock fetchCmsContentForPage jest.mock('@/helpers/cms-content-helper', () => ({ @@ -75,7 +77,7 @@ function setupMocks() { } beforeEach(() => { - useMainStore().order.bailout.bailoutCode = null; + useMainStore().applicationUser.pageData[issPageValues.BAILOUT_PAGE] = null; }); describe('policy-vehicles.vue', () => { @@ -259,11 +261,16 @@ describe('policy-vehicles.vue', () => { }); // Act - wrapper.vm.mainStore.order.bailout.bailoutCode = bailoutCode.VehicleLookupError; + wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = { + 'bailout-page': { + bailoutCode: bailoutCode.VehicleLookupError + } + }; await wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(wrapper.vm.$router, bailoutCode.VehicleLookupError, `An error occurred looking up Vin: ${vin}. Error: ${lookupReturnValue.data}`); + // eslint-disable-next-line max-len + expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(wrapper.vm.$router.currentRoute, bailoutMessage.vehicleLookupError(vin, lookupReturnValue.data)); expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, undefined, diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index c9e6ba9b..f6eb86a2 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -54,6 +54,7 @@ import endorsementOptions from '@/constants/endorsement-options.js'; import globalRules from '@/constants/global-rules.js'; import { useMainStore } from '@/store/index.js'; import bailoutCode from '@/constants/bailoutCode'; +import bailoutMessage from '@/constants/bailoutMessage'; export default { name: 'policy-vehicles', @@ -201,7 +202,7 @@ export default { return this.navigateForward(); } - this.mainStore.setBailout(this.$router, bailoutCode.VehicleLookupError, `An error occurred looking up Vin: ${vehicle.vin}. Error: ${vehicleLookupResponse.data}`); + this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.vehicleLookupError(vehicle.vin, vehicleLookupResponse.data)); return this.navigateForward(); } diff --git a/src/layouts/provider-preference/provider-preference.vue b/src/layouts/provider-preference/provider-preference.vue index 998ac47d..0dea276c 100644 --- a/src/layouts/provider-preference/provider-preference.vue +++ b/src/layouts/provider-preference/provider-preference.vue @@ -69,6 +69,7 @@ import shopPreferenceModal from '@/layouts/provider-preference/shop-preference-m import tpaRecalModal from '@/layouts/provider-preference/tpa-recal-modal/tpa-recal-modal.vue'; import globalRules from '@/constants/global-rules'; import bailoutCode from '@/constants/bailoutCode'; +import bailoutMessage from "@/constants/bailoutMessage"; const options = { SAFELITE: 'SafeliteOption', TPA: 'TPAOption' }; @@ -195,7 +196,7 @@ export default { } scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED; } else { - this.mainStore.setBailout(this.$router, bailoutCode.TPANotEnabled, 'User selected TPA when TPA is not enabled for this client'); + this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.TPANotEnabled()); scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED; } break; diff --git a/src/layouts/service-packages/service-packages.vue b/src/layouts/service-packages/service-packages.vue index 41751482..862e1496 100644 --- a/src/layouts/service-packages/service-packages.vue +++ b/src/layouts/service-packages/service-packages.vue @@ -66,6 +66,8 @@ import allGlassPartsAndItemsHavePrices from '@/layouts/service-packages/service- import globalRules from '@/constants/global-rules'; import servicePackageQuestion from '@/layouts/service-packages/service-package-question/service-package-question.vue'; import issPageValues from '@/router/router-constants/issPage-values'; +import bailoutCode from "@/constants/bailoutCode"; +import bailoutMessage from "@/constants/bailoutMessage"; const store = useMainStore(); @@ -119,29 +121,23 @@ export default { ...clonedGlassParts ]; + let hasBailedOut = false; const pricingResults = await store.getPriceOrderItems(availableLineItems) .catch((err) => { - const errorPageData = { - errorData: err.data, - functionLocation: 'beforeRouteEnter', - functionName: 'getPriceOrderItems', - functionParameters: { - availableLineItems - }, - routeFrom: from, - routeTo: to - }; - useMainStore().updatePageData({ page: issPageValues.BAILOUT_PAGE, data: errorPageData }); + useMainStore().setBailout(to, bailoutMessage.pricingResponseError(availableLineItems.map((li) => li.partNumber), { code: err.code, message: err.message, data: err.data })); + hasBailedOut = true; next(`/?issPage=${issPageValues.BAILOUT_PAGE}`); }); // Call the "next" function to complete the transition to this page. - next((vm) => { - vm.setCmsContent(resultMap.cmsContent); - vm.pricedGlassParts = clonedGlassParts; - vm.supportingItems = resultMap.supportingItems; - vm.availableLineItems = pricingResults; - }); + if (!hasBailedOut) { + next((vm) => { + vm.setCmsContent(resultMap.cmsContent); + vm.pricedGlassParts = clonedGlassParts; + vm.supportingItems = resultMap.supportingItems; + vm.availableLineItems = pricingResults; + }); + } }, data() { return { diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 44011616..386dcb5a 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -135,6 +135,7 @@ import { shallowRef } from 'vue'; import routerParams from '@/router/router-constants/router-params'; import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js'; import bailoutCode from '@/constants/bailoutCode'; +import bailoutMessage from "@/constants/bailoutMessage"; const radiusFilterPairs = [ { radius: 15, filter: '15 miles' }, @@ -326,7 +327,7 @@ export default { return getTpaProvidersResult?.data?.shopProviders ?? []; }, doNotSeeMyShopLinkClick() { - this.mainStore.setBailout(this.$router, bailoutCode.DoNotSeeMyShop, 'User does not see their shop.'); + this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.doNotSeeMyShop()); this.$router.navigate( this.navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK, this.$route diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index c96db55f..cdea5263 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -64,6 +64,7 @@ import vinLocationInformation from '@/layouts/vin-lookup/vin-location-informatio import vinLookupAlerts from '@/layouts/vin-lookup/vin-lookup-alerts/vin-lookup-alerts.vue'; import vinQuestion from '@/layouts/vin-lookup/vin-question/vin-question.vue'; import bailoutCode from '@/constants/bailoutCode'; +import bailoutMessage from "@/constants/bailoutMessage"; export default { name: 'vin-lookup', @@ -178,7 +179,7 @@ export default { if (vehicleLookupResponse.error) { this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; - this.mainStore.setBailout(this.$router, bailoutCode.VehicleNotFound, `Failed to find vehicle in system with vin: ${this.vin}`); + this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.vehicleNotFound(this.vin)); this.resetVehicleFromLookup(); this.$refs.siteFooter.removeLoader(); // Temp solution to turn on 'disabled' style on the Continue button diff --git a/src/router/index.js b/src/router/index.js index 1700a50d..4578d427 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -20,6 +20,7 @@ import bailoutCode from '@/constants/bailoutCode'; import IssPageValues from '@/router/router-constants/issPage-values'; import navigationScenarios from './router-constants/navigation-scenarios'; import canBailoutNavigateBack from "@/helpers/bailout-helper"; +import bailoutMessage from "@/constants/bailoutMessage"; const routes = [ { @@ -130,7 +131,7 @@ router.beforeEach(async (to, from) => { const store = useMainStore(); // Prevent navigating backwards if we enter a bailout that we are not allowed to go back on - if (store.isBailout && from.name === IssPageValues.BAILOUT_PAGE && to.name !== IssPageValues.CONTACT_CONFIRMATION + if (store.isBailout && from.name === IssPageValues.BAILOUT_PAGE && to.name !== 'root' && to.name !== IssPageValues.CONTACT_CONFIRMATION && !canBailoutNavigateBack()) { return false; } @@ -153,7 +154,7 @@ router.afterEach(async (to, from) => { const saveSessionSynchronous = !!from.params[routerParams.SAVE_SESSION_SYNCHRONOUS]; await saveSession({ shouldAwaitSaveSessionQueue: saveSessionSynchronous }).catch((error) => { if (from.name === issPageValues.WELCOME_PAGE) { - store.setBailout(router, bailoutCode.SaveSessionError, error.data); + store.setBailout(router, bailoutMessage.saveSessionError(error.data)); router.navigate( navigationScenarios.SAVE_SESSION_FAILED, { query: { issPage: issPageValues.WELCOME_PAGE } } diff --git a/src/store/index.js b/src/store/index.js index 4cfd45e0..96a2fc7c 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -163,13 +163,6 @@ const getDefaultState = () => ({ jobMaxMinutes: null, jobMinMinutes: null }, - bailout: { - page: null, - url: null, - bailoutCode: null, - errorMessage: null, - submit: false - }, referralNumber: null, referralDate: null, referralCorrelationId: '00000000-0000-0000-0000-000000000000', @@ -228,7 +221,7 @@ export const useMainStore = defineStore({ || state.order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP, isDropOffAppointment: (state) => state.order.serviceLocation.appointmentType === AppointmentTypeStrings.DROP_OFF, isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, - isBailout: (state) => state.order.bailout.bailoutCode !== null, + isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null, eventBusItem: (state) => (eventCategory, eventSubCategory) => { const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory); return matchedEvent?.eventValue; @@ -1156,12 +1149,6 @@ export const useMainStore = defineStore({ jobMaxMinutes: schedule.jobMaxMinutes, jobMinMinutes: schedule.jobMinMinutes }, - bailout: this.order.bailout.submit ? { - page: this.order.bailout.page, - url: this.order.bailout.url, - bailoutCode: this.order.bailout.bailoutCode, - errorMessage: this.order.bailout.errorMessage - } : null, referralDate: this.order.referralDate, referralNumber: this.order.referralNumber?.toString(), referralCorrelationId: this.order.referralCorrelationId, @@ -1419,11 +1406,10 @@ export const useMainStore = defineStore({ }, resetBailout() { - this.order.bailout.url = null; - this.order.bailout.page = null; - this.order.bailout.bailoutCode = null; - this.order.bailout.errorMessage = null; - this.order.bailout.submit = false; + this.updatePageData({ + page: issPageValues.BAILOUT_PAGE, + data: null + }); }, updateSupportingItems(partsData) { @@ -2069,11 +2055,17 @@ export const useMainStore = defineStore({ this.updateRegistration(registrationInfo); }, - setBailout(router, code, errorMessage) { - this.order.bailout.url = window.location.href; - this.order.bailout.page = router.currentRoute.value.name; - this.order.bailout.bailoutCode = code; - this.order.bailout.errorMessage = errorMessage; + setBailout(currentRoute, bailoutData) { + this.updatePageData({ + page: issPageValues.BAILOUT_PAGE, + data: { + url: window.location.href, + page: currentRoute.value?.name ?? currentRoute.name, + bailoutCode: bailoutData.code, + errorMessage: bailoutData.message, + submit: false + } + }); }, setBailoutContactInfo(contact) { @@ -2081,7 +2073,7 @@ export const useMainStore = defineStore({ this.order.customer.lastName = contact.lastName; this.order.customer.phoneNumber = contact.phoneNumber; this.order.customer.emailAddress = contact.email; - this.order.bailout.submit = true; + this.pageData(issPageValues.BAILOUT_PAGE).submit = true; }, resetRegistrationAndDependencies() { From 592e21289ed14cbfced1fe4c4307db20b5bba9e5 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 15 Jan 2024 16:09:51 -0500 Subject: [PATCH 4/9] Prepopulating vehicle selection page with content from store --- .../vehicle-question/vehicle-question.vue | 9 +++++---- .../vehicle-selection/vehicle-selection.vue | 18 ++++++++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/layouts/vehicle-selection/vehicle-question/vehicle-question.vue b/src/layouts/vehicle-selection/vehicle-question/vehicle-question.vue index 1a8e335a..aea4dd8d 100644 --- a/src/layouts/vehicle-selection/vehicle-question/vehicle-question.vue +++ b/src/layouts/vehicle-selection/vehicle-question/vehicle-question.vue @@ -32,9 +32,9 @@ export default { get() { return this.selectedIndex?.toString(); }, - set(newValue) { - this.selectedIndex = newValue; - newValue = newValue != null && newValue > -1 ? this.values[newValue] : null; + set(newIndex) { + this.selectedIndex = newIndex; + const newValue = newIndex != null && newIndex > -1 ? this.values[newIndex] : null; this.$emit('update:modelValue', newValue); } } @@ -46,7 +46,8 @@ export default { if (this.values.length === 1) { this.selectedValue = 0; } else { - this.selectedValue = null; + const valueIndex = this.values.indexOf(this.modelValue); + this.selectedValue = valueIndex === -1 ? null : valueIndex; } }, clearValues() { diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue index 6e707b49..4d0d16ed 100644 --- a/src/layouts/vehicle-selection/vehicle-selection.vue +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -134,11 +134,12 @@ export default { validationRules: String }, data() { + const { year, make, model, style } = useMainStore().order.vehicle; return { - selectedYear: null, - selectedMake: null, - selectedModel: null, - selectedStyle: null + selectedYear: year, + selectedMake: make, + selectedModel: model, + selectedStyle: style }; }, computed: { @@ -176,6 +177,15 @@ export default { }, mounted() { this.$refs.vehicleYearQuestion.getNewValues(); + if (this.selectedYear) { + this.$refs.vehicleMakeQuestion.getNewValues(); + } + if (this.selectedMake) { + this.$refs.vehicleModelQuestion.getNewValues(); + } + if (this.selectedModel) { + this.$refs.vehicleStyleQuestion.getNewValues(); + } }, methods: { arePagePrerequisitesValid() { From da3459ce4f21f7ee746489e6545f0e91801cdd53 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Tue, 16 Jan 2024 12:54:29 -0500 Subject: [PATCH 5/9] Prepopulating tpa search page when return --- src/layouts/tpa-search/tpa-search.vue | 66 +++++++++++++------- src/router/router-constants/routing-table.js | 2 +- src/store/index.js | 2 + 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index fb1cd7b1..25610da5 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -131,10 +131,8 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js'; import globalRules from '@/constants/global-rules.js'; import widgetFields from '@/constants/cms-widget-fields.js'; import { shallowRef } from 'vue'; -import routerParams from '@/router/router-constants/router-params'; import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js'; -import bailoutCode from '@/constants/bailoutCode'; -import bailoutMessage from "@/constants/bailoutMessage"; +import bailoutMessage from '@/constants/bailoutMessage'; const radiusFilterPairs = [ { radius: 15, filter: '15 miles' }, @@ -143,15 +141,35 @@ const radiusFilterPairs = [ { radius: 100, filter: '100 miles' } ]; -async function getInitialFilterAndProviders() { - const { zipCode } = useMainStore().order.customer.address; +function convertRadiusFilterToInteger(filter) { + const pair = radiusFilterPairs.find((p) => p.filter === filter); + return pair?.radius ?? 0; +} + +async function getInitialSearchData() { + const customerZipCode = useMainStore().order.customer.address.zipCode; + const serviceLocationZipCode = useMainStore().order.serviceLocation.zipCode; + const zipCode = serviceLocationZipCode ?? customerZipCode; + + const { searchFilter } = useMainStore().order.serviceLocation; + let { providerNumber } = useMainStore().order.serviceLocation.provider; - let pairIndex = 0; let providers = []; + let radius = 0; + if (searchFilter) { + radius = convertRadiusFilterToInteger(searchFilter); + const result = await useMainStore().getTpaProviders(zipCode, radius); + providers = result?.data?.shopProviders ?? []; + + return { zipCode, filter: searchFilter, providers, providerNumber }; + } + + providerNumber = null; + let pairIndex = 0; let filter = ''; while (providers.length === 0 && pairIndex < radiusFilterPairs.length) { const pair = radiusFilterPairs[pairIndex]; - const { radius } = pair; + radius = pair.radius; filter = pair.filter; // eslint-disable-next-line no-await-in-loop const result = await useMainStore().getTpaProviders(zipCode, radius); @@ -160,7 +178,7 @@ async function getInitialFilterAndProviders() { pairIndex += 1; } - return { filter, providers }; + return { zipCode, filter, providers, providerNumber }; } export default { @@ -180,21 +198,19 @@ export default { }, mixins: [BaseFormMixin], async beforeRouteEnter(to, from, next) { - const { filter, providers } = await getInitialFilterAndProviders(); + const { zipCode, filter, providers, providerNumber } = await getInitialSearchData(); const cmsContent = await fetchCmsContentForPage(to.query.issPage); next(async (vm) => { vm.setCmsContent(cmsContent); - vm.setFilter(filter); - vm.setProviders(providers); + vm.setInitialSearchData(zipCode, filter, providers, providerNumber); }); }, data() { - const { zipCode } = useMainStore().order.customer.address; return { dataLoaded: false, - zipCode, - mapZipCode: zipCode, + zipCode: null, + mapZipCode: null, filter: '', providers: [], selectedProviderNumber: '', @@ -250,8 +266,7 @@ export default { ); }, radiusInMiles() { - const pair = radiusFilterPairs.find((p) => p.filter === this.filter); - return pair?.radius ?? 0; + return convertRadiusFilterToInteger(this.filter); }, noNetworkShopsAlertHeaderText() { return this.getCmsContent( @@ -279,14 +294,18 @@ export default { } }, providers(newProviders) { - this.selectedProviderNumber = newProviders?.length === 1 ?? false - ? newProviders[0]?.providerNumber ?? '' - : ''; + if (this.dataLoaded) { + this.selectedProviderNumber = newProviders?.length === 1 ?? false + ? newProviders[0]?.providerNumber ?? '' + : ''; + } }, selectedProviderNumber(newNumber) { const provider = this.providers?.find((p) => p.providerNumber === newNumber); - if (provider) { + if (provider && this.dataLoaded) { useMainStore().updateServiceLocation({ + searchFilter: this.filter, + zipCode: this.zipCode, provider: { providerNumber: provider?.providerNumber, address: { @@ -309,11 +328,12 @@ export default { } }, methods: { - setFilter(filter) { + setInitialSearchData(zipCode, filter, providers, providerNumber) { + this.zipCode = zipCode; + this.mapZipCode = zipCode; this.filter = filter; - }, - setProviders(providers) { this.providers = providers; + this.selectedProviderNumber = providerNumber; }, getProviderAddress(provider) { const city = toTitleCase(provider?.address?.city); diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index fc1bfdb5..9891ef39 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -671,7 +671,7 @@ const routingTable = () => [ }, { scenario: navigationScenarios.EDIT_PREFERRED_SHOP, - destinationIssPageValue: issPageValues.PROVIDER_PREFERENCE + destinationIssPageValue: issPageValues.TPA_SEARCH }, { scenario: navigationScenarios.EDIT_CONTACT_DETAILS, diff --git a/src/store/index.js b/src/store/index.js index 1c2667a9..0249b80c 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -122,6 +122,7 @@ const getDefaultState = () => ({ appointmentType: null, isVehicleProtected: null, IsSafeliteProvider: null, + searchFilter: null, provider: { providerNumber: null, address: { @@ -1373,6 +1374,7 @@ export const useMainStore = defineStore({ companyName: serviceLocationInfo.provider?.companyName, phoneNumber: serviceLocationInfo.provider?.phoneNumber }; + this.order.serviceLocation.searchFilter = serviceLocationInfo.searchFilter; }, resetRegistrationState() { From 72b3879c917d461c827bf4e05a85757c1be0ed65 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Tue, 16 Jan 2024 23:13:07 -0500 Subject: [PATCH 6/9] Updating tests --- .../__snapshots__/tpa-search.spec.js.snap | 4 +- src/layouts/tpa-search/tpa-search.spec.js | 179 ++++++++++++++++-- 2 files changed, 170 insertions(+), 13 deletions(-) diff --git a/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap b/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap index b34c010c..4154d1a8 100644 --- a/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap +++ b/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap @@ -7,7 +7,7 @@ Object { }, "dataLoaded": false, "filter": "", - "mapZipCode": "12663", + "mapZipCode": null, "providers": Array [], "reloadingProviders": false, "rules": Object { @@ -202,6 +202,6 @@ Object { "siteHeader": "SiteHeaderWidget", "tpaSearchQuestion": "TPASearchQuestion", }, - "zipCode": "12663", + "zipCode": null, } `; diff --git a/src/layouts/tpa-search/tpa-search.spec.js b/src/layouts/tpa-search/tpa-search.spec.js index 30318933..56aa4e34 100644 --- a/src/layouts/tpa-search/tpa-search.spec.js +++ b/src/layouts/tpa-search/tpa-search.spec.js @@ -807,11 +807,30 @@ describe('TPA search page', () => { }); // TODO confirm tests appropriate. Also should this method be async? describe('on providers', () => { + test('does not update provider number when dataLoaded false', () => { + // Arrange + const initialValue = 1234; + const { wrapper } = getMountedComponent({}, { + dataLoaded: false, + selectedProviderNumber: initialValue, + }); + const value = 'some other value'; + const newProviders = [{ providerNumber: value }]; + + // Act + wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); + + // Assert + expect(wrapper.vm.selectedProviderNumber).toBe(initialValue); + }); test.each([null, undefined, []])( 'sets selected provider number to "" when there are no providers', (newProviders) => { // Arrange - const { wrapper } = getMountedComponent(); + const { wrapper } = getMountedComponent({}, { + dataLoaded: true, + selectedProviderNumber: null + }); // Act wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); @@ -822,7 +841,10 @@ describe('TPA search page', () => { ); test('sets selected provider number to value of provider when there is one provider', () => { // Arrange - const { wrapper } = getMountedComponent(); + const { wrapper } = getMountedComponent({}, { + dataLoaded: true, + selectedProviderNumber: null + }); const value = 'some value'; const newProviders = [{ providerNumber: value }]; @@ -834,7 +856,10 @@ describe('TPA search page', () => { }); test('sets selected provider number to "" when there are more than one provider', () => { // Arrange - const { wrapper } = getMountedComponent(); + const { wrapper } = getMountedComponent({}, { + dataLoaded: true, + selectedProviderNumber: null + }); const newProviders = [{ value: 'val1' }, { value: 'val2' }]; // Act @@ -846,10 +871,36 @@ describe('TPA search page', () => { }); describe('selectedProviderNumber', () => { describe('does not call updateServiceLocation when', () => { + test('dataLoaded is false', () => { + // Arrange + const providerNumber = 38447; + const { wrapper } = getMountedComponent({}, { + providers: [{ + providerNumber, + address: { + streetAddress: '143 Average Lane', + city: 'Cambridge', + state: 'OH', + zipCode: '72983', + zipCodeCtu: '0390' + }, + companyName: "Sally's Auto", + phoneNumber: '1234567890' + }], + dataLoaded: false + }); + + // Act + wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, providerNumber); + + // Assert + expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); + }); test('providers list is null ', () => { // Arrange const { wrapper } = getMountedComponent({}, { - providers: null + providers: null, + dataLoaded: true }); const newProviderNumber = 11235; @@ -862,7 +913,8 @@ describe('TPA search page', () => { test('providers list is empty ', () => { // Arrange const { wrapper } = getMountedComponent({}, { - providers: [] + providers: [], + dataLoaded: true }); const newProviderNumber = 11235; @@ -886,7 +938,8 @@ describe('TPA search page', () => { }, companyName: "Sally's Auto", phoneNumber: '1234567890' - }] + }], + dataLoaded: true }); const newProviderNumber = 11235; @@ -919,18 +972,26 @@ describe('TPA search page', () => { companyName, phoneNumber }; + const filter = '10 miles'; + const serviceZipCode = 10098; const { wrapper } = getMountedComponent({}, { providers: [ provider, { providerNumber: 328949832 } - ] + ], + filter, + zipCode: serviceZipCode, + dataLoaded: true }); // Act wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, newProviderNumber); // Assert + expect(wrapper.vm.mainStore.updateServiceLocation).toHaveBeenCalledTimes(1); expect(wrapper.vm.mainStore.updateServiceLocation).toHaveBeenCalledWith({ + searchFilter: filter, + zipCode: serviceZipCode, provider: { providerNumber: newProviderNumber, address: { @@ -1292,11 +1353,89 @@ describe('TPA search page', () => { }); }); describe('before route enter', () => { + test('when service location zipCode set in store, it is used', async () => { + // Arrange + const { wrapper } = getMountedComponent(); + const customerZipCode = '18394'; + const serviceZipCode = '43982'; + useMainStore().order.customer.address.zipCode = customerZipCode; + useMainStore().order.serviceLocation.zipCode = serviceZipCode; + + // Act + await tpaSearch.beforeRouteEnter.call( + wrapper.vm, + { query: { issPage: 'tpa-search' } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.zipCode).toBe(serviceZipCode); + }); + test('when service location zipCode not set in store, customer zipCode used', async () => { + // Arrange + const { wrapper } = getMountedComponent(); + const customerZipCode = '18394'; + useMainStore().order.customer.address.zipCode = customerZipCode; + useMainStore().order.serviceLocation.zipCode = null; + + // Act + await tpaSearch.beforeRouteEnter.call( + wrapper.vm, + { query: { issPage: 'tpa-search' } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.zipCode).toBe(customerZipCode); + }); + test('when search filter set in store, expected filter and provider number returned', async () => { + // Arrange + const { wrapper } = getMountedComponent(); + const customerZipCode = '18394'; + const searchFilter = '25 miles'; + const providerNumber = 749321; + useMainStore().order.customer.address.zipCode = customerZipCode; + useMainStore().order.serviceLocation.zipCode = null; + useMainStore().order.serviceLocation.searchFilter = searchFilter; + useMainStore().order.serviceLocation.provider.providerNumber = providerNumber; + const providers = [{ companyName: 'some provider' }]; + const expectedRadius = 25; + useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => + ({ data: { shopProviders: radius === expectedRadius ? providers : [] } })); + + // Act + await tpaSearch.beforeRouteEnter.call( + wrapper.vm, + { query: { issPage: 'tpa-search' } }, + undefined, + (c) => c(wrapper.vm) + ); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.filter).toBe(searchFilter); + expect(wrapper.vm.providers).toEqual(providers); + expect(wrapper.vm.selectedProviderNumber).toBe(providerNumber); + expect(useMainStore().getTpaProviders).toBeCalledTimes(1); + expect(useMainStore().getTpaProviders).toBeCalledWith(customerZipCode, expectedRadius); + }); test('when providers exist at 15 mile radius, filter is set to "15 miles" and providers set to expected', async () => { // Arrange const { wrapper } = getMountedComponent(); const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = zipCode; + useMainStore().order.customer.address.zipCode = '00001'; + useMainStore().order.serviceLocation.zipCode = zipCode; + useMainStore().order.serviceLocation.searchFilter = null; const providers = [{ companyName: 'provider' }]; useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => ({ data: { shopProviders: radius === 15 ? providers : [] } })); @@ -1314,15 +1453,19 @@ describe('TPA search page', () => { await wrapper.vm.$nextTick(); // Assert + expect(wrapper.vm.zipCode).toBe(zipCode); expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); + expect(wrapper.vm.selectedProviderNumber).toBeNull(); expect(useMainStore().getTpaProviders).toBeCalledTimes(1); }); test('when providers exist at 25 mile radius, filter is set to "25 miles" and providers set to expected', async () => { // Arrange const { wrapper } = getMountedComponent(); const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = zipCode; + useMainStore().order.customer.address.zipCode = '00001'; + useMainStore().order.serviceLocation.zipCode = zipCode; + useMainStore().order.serviceLocation.searchFilter = null; const providers = [{ companyName: 'provider' }]; useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => ({ data: { shopProviders: radius === 25 ? providers : [] } })); @@ -1340,15 +1483,19 @@ describe('TPA search page', () => { await wrapper.vm.$nextTick(); // Assert + expect(wrapper.vm.zipCode).toBe(zipCode); expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); + expect(wrapper.vm.selectedProviderNumber).toBeNull(); expect(useMainStore().getTpaProviders).toBeCalledTimes(2); }); test('when providers exist at 50 mile radius but not 25, filter is set to "50 miles" and providers set to expected', async () => { // Arrange const { wrapper } = getMountedComponent(); const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = zipCode; + useMainStore().order.customer.address.zipCode = '00001'; + useMainStore().order.serviceLocation.zipCode = zipCode; + useMainStore().order.serviceLocation.searchFilter = null; const providers = [{ companyName: 'provider' }]; useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => ({ data: { shopProviders: radius === 50 ? providers : [] } })); @@ -1366,8 +1513,10 @@ describe('TPA search page', () => { await wrapper.vm.$nextTick(); // Assert + expect(wrapper.vm.zipCode).toBe(zipCode); expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); + expect(wrapper.vm.selectedProviderNumber).toBeNull(); expect(useMainStore().getTpaProviders).toBeCalledTimes(3); }); test( @@ -1377,6 +1526,8 @@ describe('TPA search page', () => { const { wrapper } = getMountedComponent(); const zipCode = '18394'; useMainStore().order.customer.address.zipCode = zipCode; + useMainStore().order.serviceLocation.zipCode = null; + useMainStore().order.serviceLocation.searchFilter = null; const providers = [{ companyName: 'provider' }]; useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => ({ data: { shopProviders: radius === 100 ? providers : [] } })); @@ -1394,8 +1545,10 @@ describe('TPA search page', () => { await wrapper.vm.$nextTick(); // Assert + expect(wrapper.vm.zipCode).toBe(zipCode); expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); + expect(wrapper.vm.selectedProviderNumber).toBeNull(); expect(useMainStore().getTpaProviders).toBeCalledTimes(4); } ); @@ -1405,7 +1558,9 @@ describe('TPA search page', () => { // Arrange const { wrapper } = getMountedComponent(); const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = zipCode; + useMainStore().order.customer.address.zipCode = '00001'; + useMainStore().order.serviceLocation.zipCode = zipCode; + useMainStore().order.serviceLocation.searchFilter = null; useMainStore().getTpaProviders = jest.fn().mockImplementation(() => ([])); const expectedFilter = '100 miles'; @@ -1421,8 +1576,10 @@ describe('TPA search page', () => { await wrapper.vm.$nextTick(); // Assert + expect(wrapper.vm.zipCode).toBe(zipCode); expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual([]); + expect(wrapper.vm.selectedProviderNumber).toBeNull(); expect(useMainStore().getTpaProviders).toBeCalledTimes(4); } ); From deacd58008896a36eda34a2cf7e49b073ca70c0c Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Tue, 16 Jan 2024 23:36:49 -0500 Subject: [PATCH 7/9] Navigating to tpa submit after contact details when appropriate --- src/layouts/contact-details/contact-details.vue | 8 ++++---- src/layouts/tpa-submit/tpa-submit.vue | 2 +- src/router/router-constants/routing-table.js | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index 3e30c10f..0eb117bc 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -196,10 +196,10 @@ export default { notesForTechnician: this.notesForTechnician }; useMainStore().updateContactInfo(contactInfo); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD, - this.$route - ); + const scenario = useMainStore().order.serviceLocation.IsSafeliteProvider === false + ? this.navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP + : this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP; + this.$router.navigate(scenario, this.$route); } } }; diff --git a/src/layouts/tpa-submit/tpa-submit.vue b/src/layouts/tpa-submit/tpa-submit.vue index 58fbff05..1ba7f98a 100644 --- a/src/layouts/tpa-submit/tpa-submit.vue +++ b/src/layouts/tpa-submit/tpa-submit.vue @@ -208,7 +208,7 @@ export default { return [this.companyName ?? '', displayAddress, displayPhoneNumber]; }, getContactInfoLines() { - const { firstName, lastName, emailAddress, phoneNumber } = useMainStore().order.contactInfo; + const { firstName, lastName, emailAddress, phoneNumber } = useMainStore().contactInfo; return [ `${firstName} ${lastName}`, emailAddress ?? '', diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 9891ef39..2cded6f6 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -581,8 +581,12 @@ const routingTable = () => [ destinationIssPageValue: issPageValues.SCHEDULE_PAGE }, { - scenario: navigationScenarios.CLICKED_FORWARD, + scenario: navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP, destinationIssPageValue: issPageValues.SERVICE_PACKAGES + }, + { + scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, + destinationIssPageValue: issPageValues.TPA_SUBMIT } ] }, From 4b40e1ca6eea755f17abb29d31baacfc1da765b0 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Wed, 17 Jan 2024 11:29:46 -0500 Subject: [PATCH 8/9] Updating tests git push --- .../contact-details/contact-details.spec.js | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/layouts/contact-details/contact-details.spec.js b/src/layouts/contact-details/contact-details.spec.js index 9ee0dec4..7930b319 100644 --- a/src/layouts/contact-details/contact-details.spec.js +++ b/src/layouts/contact-details/contact-details.spec.js @@ -248,13 +248,25 @@ describe('contactDetails.vue', () => { expect(wrapper.vm.$router.navigateWithSpinner) .toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined); }); - test('Forward button clicked triggers navigation', () => { + test('Forward button clicked triggers appropriate navigation when safelite shop', () => { // Arrange - const wrapper = shallowMount(contactDetails, getMountOptions({ + const mountOptions = getMountOptions({ router: { navigate: jest.fn() + }, + navigationScenarios + }); + const mainInitialState = { + order: { + serviceLocation: { IsSafeliteProvider: true } } - })); + }; + mountOptions.global.plugins = [createTestingPinia({ + initialState: { + main: mainInitialState + } + })]; + const wrapper = shallowMount(contactDetails, mountOptions); // Act wrapper.vm.forwardButtonAction(); @@ -262,7 +274,35 @@ describe('contactDetails.vue', () => { // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); + .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP, undefined); + }); + test('Forward button clicked triggers appropriate navigation when TPA shop', () => { + // Arrange + const mountOptions = getMountOptions({ + router: { + navigate: jest.fn() + }, + navigationScenarios + }); + const mainInitialState = { + order: { + serviceLocation: { IsSafeliteProvider: false } + } + }; + mountOptions.global.plugins = [createTestingPinia({ + initialState: { + main: mainInitialState + } + })]; + const wrapper = shallowMount(contactDetails, mountOptions); + + // Act + wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, undefined); }); test('Forward button click updates contact info', () => { // Arrange From a81af4acc131246b3e18a9c20a06dbdd01817d42 Mon Sep 17 00:00:00 2001 From: Katie Kroell Date: Wed, 17 Jan 2024 15:49:32 -0500 Subject: [PATCH 9/9] styling changes to bailout and contact-confirmation pages --- src/layouts/coverage-statement/coverage-statement.vue | 4 ++-- src/styles/bailout-common.scss | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index fb1b826c..7e144400 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -338,8 +338,8 @@ export default { }, nextStepsBody(newValue, oldValue) { if (newValue !== oldValue) { - setupModalLink(this, 'RecalModal'); - setupModalLink(this, 'DeductibleModal'); + setupModalLinks(this, 'RecalModal'); + setupModalLinks(this, 'DeductibleModal'); } } }, diff --git a/src/styles/bailout-common.scss b/src/styles/bailout-common.scss index d7c68d2a..802506df 100644 --- a/src/styles/bailout-common.scss +++ b/src/styles/bailout-common.scss @@ -9,6 +9,10 @@ $font-size: 0.875rem; > div.sub-header-content { margin-top: 1rem; padding: 0; + + .container-fluid { + margin-bottom: 1rem; + } div.subheader-primary { padding: 0; @@ -21,6 +25,8 @@ $font-size: 0.875rem; p { line-height: 1.5rem; font-size: $font-size; + margin-bottom: 0.5rem; + color: $gray-600; &:last-child { margin-bottom: 0; }