From b55451ec9dbda5a225411383d17433e2e2872f76 Mon Sep 17 00:00:00 2001 From: Chris Redelinghuys Date: Wed, 6 May 2026 09:21:08 -0400 Subject: [PATCH 1/6] CASH-2563 --- src/constants/bailout-codes.js | 5 + .../heritage-integration/order-helper.js | 17 +- .../bailout-success/bailout-success.vue | 91 +++++++++ src/layouts/bailout/bailout.vue | 177 +++++++++++++++--- src/router/constants/navigation-scenarios.js | 5 + src/router/constants/routes.js | 8 + src/router/constants/routing-table.js | 18 ++ src/router/methods/navigate.js | 20 +- src/router/methods/routes.js | 2 + 9 files changed, 312 insertions(+), 31 deletions(-) create mode 100644 src/constants/bailout-codes.js create mode 100644 src/layouts/bailout-success/bailout-success.vue diff --git a/src/constants/bailout-codes.js b/src/constants/bailout-codes.js new file mode 100644 index 000000000..caf265292 --- /dev/null +++ b/src/constants/bailout-codes.js @@ -0,0 +1,5 @@ +const bailoutCodes = { + PARTS_NOT_FOUND: "PartsNotFound", +}; + +export { bailoutCodes }; diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index a3dc363f5..b15a0a6e3 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -57,13 +57,15 @@ export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createUnscheduledStatusWorkOrderForPIA = false, + bailoutCode = null, }) { // Add next save session to end of queue const saveSessionPromise = flushSaveSessionQueue().then(() => { return saveSessionHelper( pageNameToLog, submitAfterSave, - createUnscheduledStatusWorkOrderForPIA + createUnscheduledStatusWorkOrderForPIA, + bailoutCode ); }); @@ -106,6 +108,15 @@ export async function saveQuote({ pageNameToLog }) { return; } +export async function submitBailout({ pageNameToLog, bailoutCode }) { + await saveSession({ + pageNameToLog: pageNameToLog, + shouldAwaitSaveSessionQueue: true, + submitAfterSave: false, + bailoutCode: bailoutCode, + }); +} + // PRIVATE FUNCTIONS // /* @@ -147,13 +158,15 @@ async function loadSession( async function saveSessionHelper( pageNameToLog, submitAfterSave = false, - createUnscheduledStatusWorkOrderForPIA = false + createUnscheduledStatusWorkOrderForPIA = false, + bailoutCode = null ) { const savedSessionInfo = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.SAVE_SESSION, { submitAfterSave: submitAfterSave, createUnscheduledStatusWorkOrderForPIA: createUnscheduledStatusWorkOrderForPIA, + bailoutCode: bailoutCode, }, pageNameToLog ); diff --git a/src/layouts/bailout-success/bailout-success.vue b/src/layouts/bailout-success/bailout-success.vue new file mode 100644 index 000000000..73bbadfbb --- /dev/null +++ b/src/layouts/bailout-success/bailout-success.vue @@ -0,0 +1,91 @@ + + + diff --git a/src/layouts/bailout/bailout.vue b/src/layouts/bailout/bailout.vue index a64f7a416..754409794 100644 --- a/src/layouts/bailout/bailout.vue +++ b/src/layouts/bailout/bailout.vue @@ -1,21 +1,63 @@ diff --git a/src/router/constants/navigation-scenarios.js b/src/router/constants/navigation-scenarios.js index fef66ddbb..e4aac41c1 100644 --- a/src/router/constants/navigation-scenarios.js +++ b/src/router/constants/navigation-scenarios.js @@ -13,6 +13,11 @@ const navigationScenarios = { CLICKED_FORWARD_WITH_CASH: "CLICKED_FORWARD_WITH_CASH", CLICKED_FORWARD_WITH_INSURANCE: "CLICKED_FORWARD_WITH_INSURANCE", + // Bailout + BAILOUT: "BAILOUT", + BAILOUT_SUCCESS: "BAILOUT_SUCCESS", + CLICKED_BACK_TO_HOMEPAGE: "CLICKED_BACK_TO_HOMEPAGE", + // TODO: use virtual page? // Vin selection CLICKED_BACK_WITH_VIN: "CLICKED_BACK_WITH_VIN", diff --git a/src/router/constants/routes.js b/src/router/constants/routes.js index fc54025ef..479d0899d 100644 --- a/src/router/constants/routes.js +++ b/src/router/constants/routes.js @@ -171,6 +171,14 @@ export const routeData = { path: "/virtual/restart", virtual: true, }, + BAILOUT: { + name: "bailout", + path: "/bailout", + }, + BAILOUT_SUCCESS: { + name: "bailout-success", + path: "/bailout-success", + }, }; export const FUNNEL_START_PAGE = routeData.VEHICLE; diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index 77876a096..b85320973 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -802,6 +802,24 @@ const routingTable = function () { }, ], }, + { + pageName: routeData.BAILOUT.name, + maps: [ + { + scenario: navigationScenarios.CLICKED_FORWARD, + destinationPageData: routeData.BAILOUT_SUCCESS, + }, + ], + }, + { + pageName: routeData.BAILOUT_SUCCESS.name, + maps: [ + { + scenario: navigationScenarios.CLICKED_BACK_TO_HOMEPAGE, + destinationPageData: routeData.VEHICLE, + }, + ], + }, ]; }; diff --git a/src/router/methods/navigate.js b/src/router/methods/navigate.js index b80d31769..a6d3148f0 100644 --- a/src/router/methods/navigate.js +++ b/src/router/methods/navigate.js @@ -7,7 +7,13 @@ import store from "@/store"; import { handleSoftError } from "@/router/methods/error"; -async function navigate(scenario, currentPageName, withSaving = false, forceTopLevelNav = false) { +async function navigate( + scenario, + currentPageName, + withSaving = false, + forceTopLevelNav = false, + bailoutCode = null +) { // Check to see if calling page is same as current page. // If not, cancel navigation before it begins. const callingRoute = currentPageName; @@ -39,7 +45,7 @@ async function navigate(scenario, currentPageName, withSaving = false, forceTopL store.getters?.applicationUser?.savedSessionId || store.getters?.order?.customer?.emailAddress ) { - await saveSession({ pageNameToLog: nextPage.name }); + await saveSession({ pageNameToLog: nextPage.name, bailoutCode: bailoutCode }); } } @@ -70,9 +76,13 @@ export async function navigateWithSaving(scenario, currentPageName) { export async function navigateWithPageData(scenario, currentPageName, pageData = {}) { const nextPage = getDestination(currentPageName, scenario); - await savePageData(nextPage.name, pageData); - - return await navigate(scenario, currentPageName, true); + if (pageData && pageData.bailoutCode) { + await savePageData(currentPageName, pageData); + return await navigate(scenario, currentPageName, true, false, pageData.bailoutCode); + } else { + await savePageData(nextPage.name, pageData); + return await navigate(scenario, currentPageName, true); + } } export async function navigateAndForceTopLevelNavigation( diff --git a/src/router/methods/routes.js b/src/router/methods/routes.js index 88cc17f94..34056ba76 100644 --- a/src/router/methods/routes.js +++ b/src/router/methods/routes.js @@ -51,6 +51,8 @@ export const routes = [ createRoute(routeData.RECALIBRATION_INFO), createRoute(routeData.COVERAGE_STATEMENT), createRoute(routeData.VERIFY_DETAILS), + createRoute(routeData.BAILOUT), + createRoute(routeData.BAILOUT_SUCCESS), // Virtual pages (resolve to a non-virtual page.) createVirtualRoute(routeData.LANDING, landingBeforeEnter), createVirtualRoute(routeData.HERITAGE, heritageBeforeEnter), From dd291a980c5f7b930b6d728cf80adbde72516592 Mon Sep 17 00:00:00 2001 From: credelinghuys Date: Thu, 7 May 2026 12:52:02 -0400 Subject: [PATCH 2/6] CASH-2563 --- src/constants/bailout-codes.js | 2 +- .../heritage-integration/order-helper.js | 11 +-- .../bailout-success/bailout-success.spec.js | 56 ++++++++++++ .../bailout-success/bailout-success.vue | 4 +- src/layouts/bailout/bailout.spec.js | 20 +++++ src/layouts/bailout/bailout.vue | 87 +++++++++++++++++-- src/router/constants/routing-table.js | 2 +- src/router/methods/navigate.js | 16 ++-- src/store/index.js | 8 +- 9 files changed, 172 insertions(+), 34 deletions(-) create mode 100644 src/layouts/bailout-success/bailout-success.spec.js diff --git a/src/constants/bailout-codes.js b/src/constants/bailout-codes.js index caf265292..af6290363 100644 --- a/src/constants/bailout-codes.js +++ b/src/constants/bailout-codes.js @@ -1,5 +1,5 @@ const bailoutCodes = { - PARTS_NOT_FOUND: "PartsNotFound", + PARTS_NOT_FOUND: 10, }; export { bailoutCodes }; diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index b15a0a6e3..613f239f2 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -57,15 +57,13 @@ export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createUnscheduledStatusWorkOrderForPIA = false, - bailoutCode = null, }) { // Add next save session to end of queue const saveSessionPromise = flushSaveSessionQueue().then(() => { return saveSessionHelper( pageNameToLog, submitAfterSave, - createUnscheduledStatusWorkOrderForPIA, - bailoutCode + createUnscheduledStatusWorkOrderForPIA ); }); @@ -108,12 +106,11 @@ export async function saveQuote({ pageNameToLog }) { return; } -export async function submitBailout({ pageNameToLog, bailoutCode }) { +export async function submitBailout({ pageNameToLog }) { await saveSession({ pageNameToLog: pageNameToLog, shouldAwaitSaveSessionQueue: true, submitAfterSave: false, - bailoutCode: bailoutCode, }); } @@ -158,15 +155,13 @@ async function loadSession( async function saveSessionHelper( pageNameToLog, submitAfterSave = false, - createUnscheduledStatusWorkOrderForPIA = false, - bailoutCode = null + createUnscheduledStatusWorkOrderForPIA = false ) { const savedSessionInfo = await baseMixin.methods.dispatchStoreActionWithLogging( storeActions.SAVE_SESSION, { submitAfterSave: submitAfterSave, createUnscheduledStatusWorkOrderForPIA: createUnscheduledStatusWorkOrderForPIA, - bailoutCode: bailoutCode, }, pageNameToLog ); diff --git a/src/layouts/bailout-success/bailout-success.spec.js b/src/layouts/bailout-success/bailout-success.spec.js new file mode 100644 index 000000000..5d5f73f14 --- /dev/null +++ b/src/layouts/bailout-success/bailout-success.spec.js @@ -0,0 +1,56 @@ +// Components +import bailoutSuccess from "@/layouts/bailout-success/bailout-success.vue"; + +// Supporting Files +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; + +// Mock our module for promises. +jest.mock("@/helpers/layout-helper.js", () => ({ + settleAllPromises: jest.fn(), +})); + +// Mock fetchCmsContentForPage +jest.mock("@/helpers/cms-content-helper", () => ({ + fetchCmsContentForPage: jest.fn(), +})); + +describe("bailout-success.vue", () => { + test("renders funnelHeader component", () => { + const { wrapper } = setupMocks(); + expect(wrapper.findComponent({ name: "funnelHeader" }).exists()).toBe(true); + }); + + test("renders funnelSubHeader component", () => { + const { wrapper } = setupMocks(); + expect(wrapper.findComponent({ name: "funnelSubHeader" }).exists()).toBe(true); + }); + + test("renders Form component", () => { + const { wrapper } = setupMocks(); + expect(wrapper.findComponent({ name: "Form" }).exists()).toBe(true); + }); + + test("renders buttonMain component", () => { + const { wrapper } = setupMocks(); + expect(wrapper.findComponent({ name: "buttonMain" }).exists()).toBe(true); + }); +}); + +function setupMocks() { + const mountOptions = getMountOptions({}); + + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + }; + + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(bailoutSuccess, mountOptions); + + wrapper.vm.setCmsContent = jest.fn(); + + return { wrapper }; +} diff --git a/src/layouts/bailout-success/bailout-success.vue b/src/layouts/bailout-success/bailout-success.vue index 73bbadfbb..97f186ca6 100644 --- a/src/layouts/bailout-success/bailout-success.vue +++ b/src/layouts/bailout-success/bailout-success.vue @@ -22,7 +22,7 @@