diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index ba2e359dc..e58f51862 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -78,4 +78,5 @@ stages:
appDeployVariables:
__VUE_APP_CONSUMER_API_GATEWAY__: $(__VUE_APP_CONSUMER_API_GATEWAY__)
__VUE_APP_GOOGLE_PLACES_API_KEY__: $(__VUE_APP_GOOGLE_PLACES_API_KEY__)
+ __VUE_APP_HERITAGE_FUNNEL__: $(__VUE_APP_HERITAGE_FUNNEL__)
cfDistributionId: $(cfDistributionId)
\ No newline at end of file
diff --git a/src/.eslintrc.js b/src/.eslintrc.js
new file mode 100644
index 000000000..309a0e4fd
--- /dev/null
+++ b/src/.eslintrc.js
@@ -0,0 +1,6 @@
+module.exports = {
+ env: {
+ jest: true
+ },
+//...
+}
\ No newline at end of file
diff --git a/src/common-components/funnel-header/funnel-header.vue b/src/common-components/funnel-header/funnel-header.vue
index efe6bd06b..97c67bd40 100644
--- a/src/common-components/funnel-header/funnel-header.vue
+++ b/src/common-components/funnel-header/funnel-header.vue
@@ -68,4 +68,7 @@ export default {
.logo-image {
max-width: 78px;
}
+.alert {
+ left: 0;
+}
diff --git a/src/common-components/button-back/button-back.spec.js b/src/common-components/funnel-sub-header/button-back/button-back.spec.js
similarity index 100%
rename from src/common-components/button-back/button-back.spec.js
rename to src/common-components/funnel-sub-header/button-back/button-back.spec.js
diff --git a/src/common-components/button-back/button-back.vue b/src/common-components/funnel-sub-header/button-back/button-back.vue
similarity index 100%
rename from src/common-components/button-back/button-back.vue
rename to src/common-components/funnel-sub-header/button-back/button-back.vue
diff --git a/src/common-components/funnel-sub-header/funnel-sub-header.vue b/src/common-components/funnel-sub-header/funnel-sub-header.vue
index 155203e65..f8ac3134a 100644
--- a/src/common-components/funnel-sub-header/funnel-sub-header.vue
+++ b/src/common-components/funnel-sub-header/funnel-sub-header.vue
@@ -23,7 +23,7 @@
diff --git a/src/layouts/button-question-examples/button-question-examples.vue b/src/layouts/button-question-examples/button-question-examples.vue
deleted file mode 100644
index 6df20ddf1..000000000
--- a/src/layouts/button-question-examples/button-question-examples.vue
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
List Buttons
-
-
-
-
-
-
-
-
-
-
- (Selecting button with spinner will cause page overlay, page refresh needed to select buttons from other groups)
-
- List ButtonHorizontals
-
-
-
-
-
- List Cards
-
-
-
-
-
-
-
-
-
-
-
-
-
- Radio
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/layouts/component-test/component-test.vue b/src/layouts/component-test/component-test.vue
index 106a0c776..526b1e971 100644
--- a/src/layouts/component-test/component-test.vue
+++ b/src/layouts/component-test/component-test.vue
@@ -1144,7 +1144,7 @@
-
-
diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js
index f8b010c41..8bdace584 100644
--- a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js
+++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.spec.js
@@ -3,7 +3,7 @@ import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-que
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import store from "@/store";
-jest.mock("@/store", () => { return {}; }, {virtual: true});
+jest.mock("@/store",()=>{return{};},{virtual:true});
describe("replace-options-question.vue", () => {
test("Selected damage option is emitted upon selection.", async () => {
diff --git a/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js b/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js
index 03a96e567..eff56bbc9 100644
--- a/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js
+++ b/src/layouts/vehicle-damage/side-door-options/side-door-options.spec.js
@@ -3,7 +3,7 @@ import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-doo
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
import store from "@/store";
-jest.mock("@/store", () => { return {}; }, {virtual: true});
+jest.mock("@/store",()=>{return{};},{virtual:true});
describe("replace-options-question.vue", () => {
test("Selected side door option is emitted upon selection.", async () => {
@@ -95,7 +95,7 @@ describe("replace-options-question.vue", () => {
}) {
//Mock store
- store.dispatch = jest.fn(() => dataFromStoreApi);
+ store.dispatch = jest.fn(() => {});
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
const mountOptions = getMountOptions({
store: {
diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue
index c508e0d03..893e7d572 100644
--- a/src/layouts/vehicle-damage/vehicle-damage.vue
+++ b/src/layouts/vehicle-damage/vehicle-damage.vue
@@ -72,13 +72,15 @@ import alert from "@/ux-components/alert/alert";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import { storeActions } from "@/constants/store-actions";
-import store from "@/store";
-import baseMixin from "@/mixins/base-mixin";
import { Form, defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
+import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
+
+import store from "@/store";
+import baseMixin from "@/mixins/base-mixin";
// DEFINE VALIDATION RULES
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
@@ -139,7 +141,10 @@ export default {
},
methods: {
arePagePrerequisitesValid() {
- return store.getters.vehicle.carId !== null;
+ if(store.getters.vehicle.carId){
+ return true;
+ }
+ return false;
},
resetDependentState() {
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
@@ -271,6 +276,12 @@ export default {
},
navigateForward(partsData){
+ // CSR-98 TEMP
+ if (store.getters.vehicle.year == 2010) {
+ navigateToHeritageFunnel();
+ return;
+ }
+
//found problem questions
if (partsData.data.partsOrQuestions.some(pq => pq.partQuestions != null && pq.partQuestions.length > 0)){
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_DAMAGE_WITH_PART_QUESTIONS, this.$route, {}, {}, partsData.data);
@@ -373,15 +384,15 @@ export default {
hasSplitSingleConflict() {
if (!this.selectedWindshieldOptions.selectedWindshieldReplaceOptions) return false;
- return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
+ return this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedSingleWindshield =>
{
return selectedSingleWindshield.toUpperCase() === damageLocationsSelected.SINGLE.toUpperCase();
- }) &&
- (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedDriverWindshield =>
+ }) &&
+ (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedDriverWindshield =>
{
return selectedDriverWindshield.toUpperCase() === damageLocationsSelected.DRIVER.toUpperCase();
}) ||
- this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedPassengerWindshield =>
+ this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.some(selectedPassengerWindshield =>
{
return selectedPassengerWindshield.toUpperCase() === damageLocationsSelected.PASSENGER.toUpperCase();
})
diff --git a/src/layouts/vehicle-make/vehicle-make.vue b/src/layouts/vehicle-make/vehicle-make.vue
index 7ffb28828..4dee11115 100644
--- a/src/layouts/vehicle-make/vehicle-make.vue
+++ b/src/layouts/vehicle-make/vehicle-make.vue
@@ -75,7 +75,10 @@ export default {
);
},
arePagePrerequisitesValid() {
- return store.getters.vehicle.year !== null;
+ if (store.getters.vehicle.year){
+ return true;
+ }
+ return false;
},
resetDependentState() {
// Set
diff --git a/src/layouts/vehicle-model/vehicle-model.vue b/src/layouts/vehicle-model/vehicle-model.vue
index ea83290aa..6f57b5140 100644
--- a/src/layouts/vehicle-model/vehicle-model.vue
+++ b/src/layouts/vehicle-model/vehicle-model.vue
@@ -76,7 +76,10 @@ export default {
);
},
arePagePrerequisitesValid() {
- return store.getters.vehicle.make !== null;
+ if(store.getters.vehicle.make){
+ return true;
+ }
+ return false;
},
resetDependentState() {
// Set
diff --git a/src/layouts/vehicle-parts/vehicle-parts.spec.js b/src/layouts/vehicle-parts/vehicle-parts.spec.js
index efd69e1c5..a663c5396 100644
--- a/src/layouts/vehicle-parts/vehicle-parts.spec.js
+++ b/src/layouts/vehicle-parts/vehicle-parts.spec.js
@@ -103,7 +103,7 @@ describe("vehicle-parts.vue", () => {
test("PageData / isRepair populated in Vuex. arePagePrerequisitesValid should be true ", async () => {
//Arrange
- store.getters.pageData.mockReturnValueOnce(basePartResponse);
+ store.getters.pageData.mockReturnValue(basePartResponse);
store.getters.lineItems = { glassParts: {} }
const { wrapper } = setupMocks({
diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue
index 87dc02f0d..d4c3e164a 100644
--- a/src/layouts/vehicle-parts/vehicle-parts.vue
+++ b/src/layouts/vehicle-parts/vehicle-parts.vue
@@ -4,20 +4,32 @@
@@ -32,18 +44,10 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import alert from "@/ux-components/alert/alert";
// Supporting Files
-import {
- fetchCmsContentForPage
-} from "@/helpers/cms-content-helper";
-import {
- settleAllPromises
-} from "@/helpers/layout-helper";
-import {
- fmgPageValues
-} from "@/router/router-constants/fmgPage-values";
-import {
- storeMutations
-} from "@/constants/store-mutations";
+import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
+import { settleAllPromises } from "@/helpers/layout-helper";
+import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
+import { storeMutations } from "@/constants/store-mutations";
import store from "@/store";
export default {
@@ -71,148 +75,151 @@ export default {
})
);
- // Set alertData for the page alert. These use props so we don't call
- // initializeComponent here.
- vm.alertWidgetData = {
- copy: resultMap.cmsContent.AlertWidget.BodyText,
- headline: resultMap.cmsContent.AlertWidget.HeadlineText,
- };
- });
- },
- data() {
+ // Set alertData for the page alert. These use props so we don't call
+ // initializeComponent here.
+ vm.alertWidgetData = {
+ copy: resultMap.cmsContent.AlertWidget.BodyText,
+ headline: resultMap.cmsContent.AlertWidget.HeadlineText,
+ };
+ });
+ },
+ data() {
+ return {
+ glassParts: {},
+ alertWidgetData: Object,
+ };
+ },
+ components: {
+ glassPartQuestion,
+ funnelHeader,
+ vehicleBanner,
+ funnelSubHeader,
+ funnelFooter,
+ alert,
+ },
+ computed: {
+ PartsForQuestions() {
+ const partsData = this.PartsFromApi;
+
+ // Map API result data, to vehicle-parts data structure
+ const mappedData = partsData.partsOrQuestions.map((g) => {
return {
- glassParts: {},
- alertWidgetData: Object,
+ glassName: g.glassName,
+ glassLocation: g.glassLocation,
+ colorAnswers: g.parts.reduce((arr, p) => {
+ arr.push({
+ ColorAnswerText: p.color,
+ FeatureAnswers: [
+ {
+ FeatureAnswerText:
+ p.description === "" ? p.color : p.description,
+ PartNumber: p.partNumber,
+ },
+ ],
+ });
+ return arr;
+ }, []),
};
+ });
+
+ return mappedData;
},
- components: {
- glassPartQuestion,
- funnelHeader,
- vehicleBanner,
- funnelSubHeader,
- funnelFooter,
- alert,
+
+ PartsFromApi() {
+ return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
},
- computed: {
- PartsForQuestions() {
- const partsData = this.PartsFromApi;
- // Map API result data, to vehicle-parts data structure
- const mappedData = partsData.partsOrQuestions.map((g) => {
-
- return {
- glassName: g.glassName,
- glassLocation: g.glassLocation,
- colorAnswers: g.parts.reduce((arr, p) => {
- arr.push({
- ColorAnswerText: p.color,
- FeatureAnswers: [{
- FeatureAnswerText: p.description === "" ? p.color : p.description,
- PartNumber: p.partNumber,
- }, ],
- });
- return arr;
- }, []),
- };
- });
-
- return mappedData;
- },
-
- PartsFromApi() {
- return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
- },
-
- RefPrefix() {
- return "partQuestion";
- },
+ RefPrefix() {
+ return "partQuestion";
},
- methods: {
- arePagePrerequisitesValid() {
- // Check if isRepair is populated and if the pageData we need is here (Parts data)
- if (
- store.getters.damage.isRepair !== null &&
- Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
- .length !== 0
- ) {
- return true;
- }
+ },
+ methods: {
+ arePagePrerequisitesValid() {
+ // Check if isRepair is populated and if the pageData we need is here (Parts data)
+ if (
+ (store.getters.damage.isRepair != null) &&
+ Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
+ .length !== 0
+ ) {
+ return true;
+ }
- return false;
- },
- backButtonAction() {
- this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
- },
- forwardButtonAction() {
- const selectedGlassPartNumbers = [];
- const matchedParts = [];
+ return false;
+ },
+ backButtonAction() {
+ this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
+ },
+ forwardButtonAction() {
+ const selectedGlassPartNumbers = [];
+ const matchedParts = [];
- // Compile all selected parts from the page.
- for (let [key, value] of Object.entries(this.glassParts)) {
- for (let [glassKey, glassValue] of Object.entries(value)) {
- selectedGlassPartNumbers.push(glassValue[0]);
- }
- }
-
- // Match them to the parts from the API.
- for (let [key, value] of Object.entries(
- this.PartsFromApi.partsOrQuestions
- )) {
- for (let [partKey, partValue] of Object.entries(value.parts)) {
- const currentPart =
- this.PartsFromApi.partsOrQuestions[key].parts[partKey];
- const isMatched = selectedGlassPartNumbers.some(
- (p) => p === currentPart.partNumber
- );
-
- if (isMatched) {
- matchedParts.push(currentPart);
- }
- }
- }
-
- // If no parts could be matched, throw an error.
- if (matchedParts.length === 0) {
- throw new Error("Could not match any parts to the selected parts");
- }
-
- // Save parts to the store.
- store.commit(storeMutations.UPDATE_PARTS, matchedParts);
-
- // Navigate to the next page.
- this.$router.navigateAfterSave(
- this.navigationScenarios.SELECTED_PARTS,
- this.$route
- );
- },
-
- resetDependentState() {
- // Nothing additional to reset here: The page save is already fully resetting all the line-items on the order
- },
-
- LoadInitialPartsData() {
- const partsData = this.PartsFromApi;
- const alreadyPopulatedPartsData =
- this.$store.getters.lineItems.glassParts === null ? {} :
- this.$store.getters.lineItems.glassParts;
-
- partsData.partsOrQuestions.map((g) => {
- // If the part is already populated, use the value from the store and populate the v-model.
- Object.keys(alreadyPopulatedPartsData).forEach((key) => {
- const partNumber = alreadyPopulatedPartsData[key].partNumber;
- g.parts.forEach((p) => {
- if (p.partNumber === partNumber) {
- this.glassParts[g.glassLocation + "-" + g.glassName] = {
- [g.glassLocation]: [partNumber],
- };
- }
- });
- });
- });
+ // Compile all selected parts from the page.
+ for (let [key, value] of Object.entries(this.glassParts)) {
+ for (let [glassKey, glassValue] of Object.entries(value)) {
+ selectedGlassPartNumbers.push(glassValue[0]);
}
+ }
+
+ // Match them to the parts from the API.
+ for (let [key, value] of Object.entries(
+ this.PartsFromApi.partsOrQuestions
+ )) {
+ for (let [partKey, partValue] of Object.entries(value.parts)) {
+ const currentPart =
+ this.PartsFromApi.partsOrQuestions[key].parts[partKey];
+ const isMatched = selectedGlassPartNumbers.some(
+ (p) => p === currentPart.partNumber
+ );
+
+ if (isMatched) {
+ matchedParts.push(currentPart);
+ }
+ }
+ }
+
+ // If no parts could be matched, throw an error.
+ if (matchedParts.length === 0) {
+ throw new Error("Could not match any parts to the selected parts");
+ }
+
+ // Save parts to the store.
+ store.commit(storeMutations.UPDATE_PARTS, matchedParts);
+
+ // Navigate to the next page.
+ this.$router.navigateAfterSave(
+ this.navigationScenarios.SELECTED_PARTS,
+ this.$route
+ );
},
- mounted(){
- this.LoadInitialPartsData();
- }
+
+ resetDependentState() {
+ // Nothing additional to reset here: The page save is already fully resetting all the line-items on the order
+ },
+
+ LoadInitialPartsData() {
+ const partsData = this.PartsFromApi;
+ const alreadyPopulatedPartsData =
+ this.$store.getters.lineItems.glassParts === null
+ ? {}
+ : this.$store.getters.lineItems.glassParts;
+
+ partsData.partsOrQuestions.map((g) => {
+ // If the part is already populated, use the value from the store and populate the v-model.
+ Object.keys(alreadyPopulatedPartsData).forEach((key) => {
+ const partNumber = alreadyPopulatedPartsData[key].partNumber;
+ g.parts.forEach((p) => {
+ if (p.partNumber === partNumber) {
+ this.glassParts[g.glassLocation + "-" + g.glassName] = {
+ [g.glassLocation]: [partNumber],
+ };
+ }
+ });
+ });
+ });
+ },
+ },
+ mounted() {
+ this.LoadInitialPartsData();
+ },
};
diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue
index a27f32047..e98cc400d 100644
--- a/src/layouts/vehicle-style/vehicle-style.vue
+++ b/src/layouts/vehicle-style/vehicle-style.vue
@@ -87,7 +87,10 @@ export default {
);
},
arePagePrerequisitesValid() {
- return store.getters.vehicle.model !== null;
+ if(store.getters.vehicle.model){
+ return true;
+ }
+ return false;
},
resetDependentState() {
// Invokes
diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js
index b3b3a19f9..b5a954c43 100644
--- a/src/mixins/base-mixin.js
+++ b/src/mixins/base-mixin.js
@@ -2,7 +2,6 @@ import store from "@/store";
import { storeActions } from "@/constants/store-actions.js";
import { storeMutations } from "@/constants/store-mutations.js";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
-import { widgetNames } from "@/constants/widget-names.js";
import { vehicleCategories } from "@/constants/vehicle-categories.js";
export default {
@@ -52,9 +51,6 @@ export default {
navigationScenarios() {
return navigationScenarios;
},
- widgetNames() {
- return widgetNames;
- },
vehicleCategories() {
return vehicleCategories;
},
diff --git a/src/mixins/base-mixin.spec.js b/src/mixins/base-mixin.spec.js
index d32163469..48af9a7c0 100644
--- a/src/mixins/base-mixin.spec.js
+++ b/src/mixins/base-mixin.spec.js
@@ -1,6 +1,5 @@
import baseMixin from "@/mixins/base-mixin";
import { storeActions } from "@/constants/store-actions.js";
-import { widgetNames } from "@/constants/widget-names.js";
import { storeMutations } from "@/constants/store-mutations.js";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
import { vehicleCategories } from "@/constants/vehicle-categories.js";
@@ -67,18 +66,6 @@ describe("baseMixin.js", () => {
// Assert
expect(navigationScenariosForTest).toEqual(navigationScenarios);
});
-
- test("computed: widgetNames should be equal to import object", () => {
- // Arrange
- const mixIn = getMixInInstance({});
-
- // Act
- let widgetNamesForTest = mixIn.computed.widgetNames();
-
- // Assert
- expect(widgetNamesForTest).toEqual(widgetNames);
- });
-
test("computed: vehicleCategories should be equal to import object", () => {
// Arrange
const mixIn = getMixInInstance({});
@@ -131,7 +118,6 @@ function getMixInInstance({ isDispatchSuccess = true }) {
const baseMixIn = baseMixin;
baseMixIn.methods.$route = route;
baseMixIn.methods.storeActions = storeActions;
- baseMixIn.methods.widgetNames = widgetNames;
baseMixIn.methods.vehicleCategories = vehicleCategories;
store.dispatch = storeDispatch;
diff --git a/src/router/index.js b/src/router/index.js
index be434e9d8..778c55cfc 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -1,19 +1,23 @@
// Supporting files
import { createWebHistory, createRouter } from "vue-router";
import { storeActions } from "@/constants/store-actions";
-import baseMixin from "@/mixins/base-mixin";
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
import { routingTable } from "@/router/router-constants/routing-table.js";
import { globalEvents, globalEventTypes } from "@/constants/events";
+
+// Heritage integration
+import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
+import { updateOrCreateFunnelCookie,getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
+import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
+import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel} from "@/helpers/heritage-integration/navigation-helper";
+
+import baseMixin from "@/mixins/base-mixin";
import eventBus from "@/helpers/event-bus/event-bus";
import store from "@/store";
// Components
import ComponentTest from "@/layouts/component-test/component-test.vue";
-import AddressPOC from "@/layouts/address-poc/address-poc.vue";
import FormTest from "@/layouts/form-test/form-test.vue";
-import NestedRadio from "@/layouts/nested-radio-poc/nested-radio.vue";
-import buttonQuestionExamples from "@/layouts/button-question-examples/button-question-examples.vue"
const routes = [
{
@@ -26,26 +30,6 @@ const routes = [
name: "FormTest",
component: FormTest,
},
- {
- path: "/address-poc", // This is a temporary route for testing.
- name: "AddressPOC",
- component: AddressPOC,
- },
- {
- path: "/form-test", // This is a temporary route for testing.
- name: "FormTest",
- component: FormTest,
- },
- {
- path: "/nested-radio", // This is a temporary route for testing.
- name: "NestedRadio",
- component: NestedRadio,
- },
- {
- path: "/button-question-examples", // This is a temporary route for testing.
- name: "buttonQuestionExamples",
- component: buttonQuestionExamples,
- },
{
path: "/",
name: "root",
@@ -55,9 +39,35 @@ const routes = [
await GoToFunnelStartOn404(next);
} else {
try {
+
+ // If the saved session has timed out, clear the session, execute 404 logic.
+ if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
+ await GoToFunnelStartOn404(next);
+ }
+
+
+
+ // On entering the funnel "fresh", read cookie information, decide what to do next.
+ if (from.redirectedFrom === undefined) {
+ const loadOrderResponse = await loadOrderIfPresent();
+ const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
+
+ // If getPageToRouteExistingOrderTo determines that the return user needs to
+ // go back to heritage funnel, send them there and stop our current navigation.
+ if (pageToRedirectTo === 'heritage') {
+ await navigateToHeritageFunnel();
+ return next(false);
+ }
+
+ // Assign our fmgPage so it will load normally like the other pages.
+ to.query.fmgPage = pageToRedirectTo;
+ }
+
+ // Process funnel cookie.
+ updateOrCreateFunnelCookie();
+
// If we already have our route, go to it.
if (router.hasRoute(to.query.fmgPage)) {
-
// Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function.
let component = router.getRoutes().filter((x) => x.name === to.query.fmgPage)[0].components;
@@ -123,10 +133,14 @@ router.navigateAfterSave = (scenario, currentRoute, optionalQuery = {}, optional
navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData);
}
+router.navigateToExternalUrl = (url, optionalQuery = {}) => {
+ navigateToUrl(url, optionalQuery);
+}
+
// PRIVATE FUNCTIONS
// Navigate to the next route, depending on the scenario.
-function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
+async function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
if (!scenario) {
console.error("No scenario provided. Please review the routing table.");
return;
@@ -134,8 +148,9 @@ function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {},
// Match our maps up and navigate if we have a destination.
const matchingScenarioMap = getNavigationMap(scenario, currentRoute);
+ const destinationFmgPageValue = matchingScenarioMap.destinationFmgPageValue;
- if (matchingScenarioMap.destinationFmgPageValue !== undefined) {
+ if (destinationFmgPageValue !== undefined) {
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
// If we need to do invalidation
@@ -146,17 +161,22 @@ function navigate(scenario, currentRoute, invalidateOnSave, optionalQuery = {},
}
// Append page data to the store for the NEXT page, if any. It will be an empty object if none is provided.
- baseMixin.methods.savePageDataToStore(matchingScenarioMap.destinationFmgPageValue, optionalPageData);
+ baseMixin.methods.savePageDataToStore(destinationFmgPageValue, optionalPageData);
+
+ // if cookie and referralNumber/Date exists
+ if (getFunnelCookie()?.ReferralNumber && getFunnelCookie()?.ReferralDate) {
+ await saveOrder();
+ }
router.push({
name: "root",
query: Object.assign(optionalQuery, {
- fmgPage: matchingScenarioMap.destinationFmgPageValue,
+ fmgPage: destinationFmgPageValue,
}),
- params: optionalParams,
+ params: optionalParams
});
} else if (matchingScenarioMap.destinationUrl !== undefined) {
- navigateToUrl(matchingScenarioMap.destinationUrl);
+ navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery);
}
}
@@ -177,9 +197,15 @@ function getNavigationMap(scenario, currentRoute) {
//---------------------------------------------------------- Private Functions ----------------------------------------------------------
// Navigate to an external url.
-function navigateToUrl(url) {
+function navigateToUrl(url, optionalQuery = {}) {
// possibly show some loading screen in the future here.
- window.location.assign(url);
+ var externalUrl = new URL(url);
+
+ for (const queryKey in optionalQuery) {
+ externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
+ }
+
+ window.location.assign(externalUrl);
}
// Get route information by page name.
diff --git a/src/router/router-constants/externalUrl-values.js b/src/router/router-constants/externalUrl-values.js
new file mode 100644
index 000000000..a5b059480
--- /dev/null
+++ b/src/router/router-constants/externalUrl-values.js
@@ -0,0 +1,5 @@
+const externalUrls = {
+ HERITAGE_FUNNEL: process.env.VUE_APP_HERITAGE_FUNNEL,
+};
+
+export { externalUrls };
\ No newline at end of file
diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js
index e55385943..da170da53 100644
--- a/src/router/router-constants/navigation-scenarios.js
+++ b/src/router/router-constants/navigation-scenarios.js
@@ -4,6 +4,7 @@ const navigationScenarios = {
SELECTED_MAKE: "SELECTED_MAKE",
SELECTED_STYLE: "SELECTED_STYLE",
CLICKED_BACK: "CLICKED_BACK",
+ CLICKED_FORWARD: "CLICKED_FORWARD",
SELECTED_PARTS: "SELECTED_PARTS",
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
diff --git a/src/router/router-constants/router-parameter-keys.js b/src/router/router-constants/router-parameter-keys.js
deleted file mode 100644
index 0c75eb738..000000000
--- a/src/router/router-constants/router-parameter-keys.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const routerParameterKeys = { };
-
- export { routerParameterKeys };
\ No newline at end of file
diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js
index c30afcc7e..99583b975 100644
--- a/src/router/router-constants/routing-table.js
+++ b/src/router/router-constants/routing-table.js
@@ -1,5 +1,6 @@
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
+import { applicationConfig } from "@/constants/application-config";
const routingTable = [
{
@@ -59,7 +60,7 @@ const routingTable = [
},
{
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_SINGLE_PART,
- destinationFmgPageValue: fmgPageValues.REVEAL,
+ destinationFmgPageValue: fmgPageValues.REVEAL
},
{
scenario: navigationScenarios.SELECTED_DAMAGE_WITH_MULTIPLE_PARTS,
@@ -80,6 +81,10 @@ const routingTable = [
},
{
scenario: navigationScenarios.SELECTED_PARTS,
+ destinationFmgPageValue: fmgPageValues.QUOTE,
+ },
+ {
+ scenario: navigationScenarios.CLICKED_FORWARD,
destinationFmgPageValue: fmgPageValues.REVEAL,
},
],
diff --git a/src/store/index.js b/src/store/index.js
index 18593c897..099e8dbcf 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,11 +1,10 @@
import { createStore } from "vuex";
import { endpoints } from "@/constants/endpoints.js";
import { storeMutations } from "@/constants/store-mutations";
+import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
import createPersistedState from "vuex-persistedstate";
import globalMethods from "@/global-methods";
-
-
// Export State
export const state = {
order: {
@@ -29,28 +28,35 @@ export const state = {
firstName: null,
lastName: null,
},
+ serviceLocation: {
+ zip: null,
+ },
+ customer: {
+ emailAddress: null,
+ },
+ damage: {
+ isRepair: null,
+ numberOfChips: null,
+ glassToReplace: null,
+ },
+ lineItems: {
+ glassParts: null,
+ otherParts: null
+ },
+ referralNumber: null,
+ referralDate: null,
+ referralCorrelationId: null,
+ parentAccountNumber: null,
},
- serviceLocation: {
- zip: null,
+ applicationUser: {
+ eventBus: [],
+ pageData: {},
+ savedSessionTimeout: getDateForSavedSessionTimeout()
},
- customer: {
- emailAddress: null,
- },
- damage: {
- isRepair: null,
- numberOfChips: null,
- glassToReplace: null,
- },
- lineItems:{
- glassParts: {},
- otherParts: {}
- }
- },
- applicationUser: {
- eventBus: [],
- pageData: {}
- },
-}
+ }
+};
+
+export const state = getDefaultState();
// Export Mutations
export const mutations = {
@@ -88,46 +94,32 @@ export const mutations = {
updateIsRepair(state, isRepair){
state.order.damage.isRepair = isRepair;
},
- updateNumberOfChips(state, numberOfChips){
+ updateNumberOfChips(state, numberOfChips) {
state.order.damage.numberOfChips = numberOfChips;
},
- updateGlassToReplace(state, glassToReplace){
+ updateGlassToReplace(state, glassToReplace) {
state.order.damage.glassToReplace = glassToReplace;
},
- updateParts(state, partsData){
+ updateParts(state, partsData) {
state.order.lineItems.glassParts = partsData;
},
- updatePageData(state, pageData){
+ updatePageData(state, pageData) {
state.applicationUser.pageData[pageData.page] = pageData.data;
},
- updateRegistrationAddress(state, registrationAddress){
- state.order.vehicle.registration.address = registrationAddress;
+ updateReferralCorrelationId(state, referralCorrelationId) {
+ state.order.referralCorrelationId = referralCorrelationId;
},
- updateRegistrationCity(state, registrationCity){
- state.order.vehicle.registration.city = registrationCity;
+ updateReferralNumber(state, referralNumber) {
+ state.order.referralNumber = referralNumber;
},
- updateRegistrationState(state, registrationState){
- state.order.vehicle.registration.state = registrationState;
+ updateReferralDate(state, referralDate) {
+ state.order.referralDate = referralDate;
},
- updateRegistrationZipCode(state, registrationZipCode){
- state.order.vehicle.registration.zipCode = registrationZipCode;
- },
- updateRegistrationFirstName(state, registrationFirstName){
- state.order.vehicle.registration.firstName = registrationFirstName;
- },
- updateRegistrationLastName(state, registrationLastName){
- state.order.vehicle.registration.lastName = registrationLastName;
- },
- updateRegistrationLicensePlate(state, registrationLicensePlate){
- state.order.vehicle.registration.licensePlate = registrationLicensePlate;
- },
- updateServiceLocationZip(state, serviceLocationZip){
- state.order.vehicle.serviceLocation.zip = serviceLocationZip;
- },
- updateCustomerEmailAddress(state, customerEmailAddress){
- state.order.vehicle.customer.emailAddress = customerEmailAddress;
+ updateParentAcctNumber(state, parentAcctNumber) {
+ state.order.parentAccountNumber = parentAcctNumber;
},
+
// EVENT BUS MUTATIONS
addEventToBus(state, event) {
state.applicationUser.eventBus.push(event);
@@ -172,6 +164,33 @@ export const mutations = {
resetPartsState(state) {
state.order.lineItems.glassParts = null;
state.order.lineItems.otherParts = null;
+ },
+ resetState(state) {
+ Object.assign(state, getDefaultState());
+ },
+
+ // Misc Mutations
+ updateStateWithOrderInformation(state, orderInformation) {
+ state.order.referralNumber = orderInformation.referralNumber;
+ state.order.referralDate = orderInformation.referralDate;
+ state.order.referralCorrelationId = orderInformation.referralCorrelationId;
+ state.order.vehicle = {
+ year: orderInformation.vehicle?.year,
+ make: orderInformation.vehicle?.make,
+ model: orderInformation.vehicle?.model,
+ style: orderInformation.vehicle?.style,
+ carId: orderInformation.vehicle?.carId,
+ category: orderInformation.vehicle?.category,
+ imageUrl: orderInformation.vehicle?.imageUrl,
+ imageVifNumber: orderInformation.vehicle?.imageVifNumber,
+ imageColor: orderInformation.vehicle?.imageVifColor
+ };
+ state.order.damage.glassToReplace = orderInformation.glassToReplace;
+ state.order.damage.isRepair = orderInformation.isRepair;
+ state.order.damage.numberOfChips = orderInformation.numberOfChips;
+ state.order.lineItems.glassParts = orderInformation.parts;
+ state.order.parentAccountNumber = orderInformation.parentAccountNumber;
+ state.order.serviceLocation.zipCode = orderInformation.zipCode; // TODO CSR-416 Make sure this is correct
}
}
@@ -191,7 +210,9 @@ export const getters = {
lineItems: (state) => state.order.lineItems,
pageData: (state) => (page) => {
return state.applicationUser.pageData[page];
- }
+ },
+ applicationUser: (state) => state.applicationUser,
+ order: (state) => state.order,
}
// Export Actions
@@ -298,6 +319,9 @@ export const actions = {
resetPartsAndDependencies(context) {
context.commit(storeMutations.RESET_PARTS_STATE);
},
+ resetState(context) {
+ context.commit(storeMutations.RESET_STATE);
+ },
// Content API Actions
getRouteInfo(context, { pageName }) {
@@ -330,6 +354,13 @@ export const actions = {
});
},
+ // Misc Actions
+ setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId }) {
+ context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
+ context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
+ context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
+ },
+
// Parts API Actions
getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) {
return globalMethods.callHttpClient({
@@ -342,6 +373,46 @@ export const actions = {
vin: vin
},
});
+ },
+
+ // Order API Actions
+ saveOrder(context) {
+ const vehicle = context.getters.vehicle;
+ const damage = context.getters.damage;
+
+ return globalMethods.callHttpClient({
+ method: endpoints.SaveOrder.method,
+ endpoint: endpoints.SaveOrder.url,
+ payload: {
+ vehicle: {
+ carId: vehicle.carId,
+ year: vehicle.year,
+ make: vehicle.make,
+ model: vehicle.model,
+ style: vehicle.style,
+ },
+ numberOfChips: damage.numberOfChips,
+ zipCode: 43215, // TODO CSR-416, should not be hardcoded (state.order.serviceLocation.zipCode)
+ glassToReplace: damage.glassToReplace,
+ referralNumber: context.state.order.referralNumber,
+ referralDate: context.state.order.referralDate
+ },
+ });
+ },
+
+ loadOrder(context, { referralNumber, referralDate, referralCorrelationId }) {
+ return globalMethods.callHttpClient({
+ method: endpoints.LoadOrder.method,
+ endpoint: endpoints.LoadOrder.url,
+ payload: {
+ referralNumber: referralNumber,
+ referralDate: referralDate,
+ referralCorrelationId: referralCorrelationId
+ },
+ }).then((response) => {
+ context.commit(storeMutations.SET_LOAD_FUNNEL_SESSION_INFO, response.data);
+ return response;
+ });
}
}
@@ -357,3 +428,5 @@ export default createStore({
getters,
actions,
});
+
+// Private Functions
diff --git a/src/store/store.spec.js b/src/store/store.spec.js
index a3f8a5cb5..5c7e0c881 100644
--- a/src/store/store.spec.js
+++ b/src/store/store.spec.js
@@ -200,6 +200,38 @@ describe("Mutations", () => {
expect(storeState.applicationUser.pageData['vehicle-year']).toEqual({});
});
+ it("updateStateWithOrderInformation, should set order information in state", () => {
+ // Arrange
+ const storeState = state;
+
+ // Act
+ mutations.updateStateWithOrderInformation(storeState, {
+ referralNumber: 123,
+ referralDate: new Date().toUTCString(),
+ referralCorrelationId: "xxx-xxx-xxx",
+ vehicle: {
+ year: "2019",
+ make: "Acura",
+ model: "ILX",
+ style: "4 DOOR SEDAN",
+ carId: "C0000001",
+ category: "CAR"
+ },
+ glassToReplace: ["Windshield"],
+ isRepair: false,
+ numberOfChips: 0,
+ parts: [],
+ parentAccountNumber: "123456789",
+ });
+
+ // Assert
+ expect(storeState.order.referralNumber).toEqual(123);
+ expect(storeState.order.referralCorrelationId).toEqual("xxx-xxx-xxx");
+ expect(storeState.order.vehicle.year).toEqual("2019");
+ expect(storeState.order.vehicle.make).toEqual("Acura");
+ expect(storeState.order.vehicle.model).toEqual("ILX");
+ });
+
});
describe("Actions", () => {
@@ -466,6 +498,66 @@ describe("Actions", () => {
expect(response.data).toEqual({ imageUrl: "https://test.com" });
});
+ it("saveOrder action, returns order information", async () => {
+ // Arrange
+ const context = state;
+
+ context.getters = {
+ vehicle: {},
+ damage: {},
+ };
+ context.state = {
+ order: {}
+ };
+
+ globalMethods.callHttpClient.mockImplementation(() => {
+ return Promise.resolve({ data: { referralNumber: 123 } });
+ });
+
+ // Act
+ const response = await actions.saveOrder(context);
+
+ // Assert
+ expect(response.data).toEqual({ referralNumber: 123 });
+ });
+
+
+ it("loadOrder action, returns order information, calls mutation", async () => {
+ // Arrange
+ const context = state;
+
+ globalMethods.callHttpClient.mockImplementation(() => {
+ return Promise.resolve({ data: { referralNumber: 123 } });
+ });
+
+ const commit = jest.fn();
+
+ context.commit = commit;
+
+ // Act
+ const response = await actions.loadOrder(context, {referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx"});
+
+ // Assert
+ expect(response.data).toEqual({ referralNumber: 123 });
+ expect(commit).toBeCalledWith(storeMutations.SET_LOAD_FUNNEL_SESSION_INFO, {"referralNumber": 123});
+ });
+
+ it("setReferralInformation, should call commit three times", () => {
+ // Arrange
+ const context = state;
+ const commit = jest.fn();
+
+ context.commit = commit;
+
+ // Act
+ actions.setReferralInformation(context, {referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx"});
+
+ // Assert
+ expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_NUMBER, "123");
+ expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_DATE, new Date().toUTCString());
+ expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, "xxx-xxx-xxx");
+ });
+
});
describe("Getters", () => {
diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss
index 323430dfe..fe1637e43 100644
--- a/src/styles/common-error-styles.scss
+++ b/src/styles/common-error-styles.scss
@@ -3,14 +3,19 @@
&.list-card {
border: 1px solid $red;
color: $red;
- label {
- box-shadow: 0 0 1px $red !important;
- border-radius: .5rem;
- }
input[type=checkbox]:focus + label,
input[type=radio]:focus + label {
box-shadow: 0 0 0 2.5px $red;
}
+ input[type=checkbox]:focus + label:hover,
+ input[type=radio]:focus + label {
+ box-shadow: 0 0 0 1px $red;
+ }
+ &:hover {
+ box-shadow: 0px 0px 0px 4px $red-200;
+ border-radius: 10px !important;
+ border: 1px solid $red;
+ }
}
input[type=checkbox]:checked + label,
@@ -21,6 +26,9 @@
color: $red;
label {
border: 1px solid $red;
+ &:hover {
+ box-shadow: 0px 0px 0px 4px $red-200;
+ }
}
input[type=checkbox]:focus + label,
input[type=radio]:focus + label {
diff --git a/src/ux-components/button-main/button-main.vue b/src/ux-components/button-main/button-main.vue
index 84ed98a1a..df422234d 100644
--- a/src/ux-components/button-main/button-main.vue
+++ b/src/ux-components/button-main/button-main.vue
@@ -61,6 +61,9 @@ export default {
@media (hover: hover) {
background: linear-gradient(270deg, $blue-500 0%, $blue-700 100%);
}
+ &:focus {
+ box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
+ }
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
@@ -91,6 +94,7 @@ export default {
&.has-loader {
color: $white;
background: $blue-700;
+ box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
}
&.delay {// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue
index 68aceec51..cef3d765c 100644
--- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue
+++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue
@@ -161,6 +161,7 @@ export default {
}
}
label {
+ outline: none;
position: relative;
background: $white;
transition: all 150ms linear;
diff --git a/src/ux-components/list-button/list-button.vue b/src/ux-components/list-button/list-button.vue
index caef528de..775b19ff4 100644
--- a/src/ux-components/list-button/list-button.vue
+++ b/src/ux-components/list-button/list-button.vue
@@ -128,9 +128,9 @@ export default {
fieldOptions['initialValue'] = fieldOptions.potentialInitialValue;
}
- const {
- checked,
- handleChange,
+ const {
+ checked,
+ handleChange,
errors,
} = useField(props.groupName, props.validationRules, fieldOptions);
@@ -147,6 +147,7 @@ export default {