router/index.js assumes a load-session successful response should always go to heritage. This may have been true in the initial version of NextGen but now we can load and direct it to the correct page. The incorrect routing to heritage was also failing here for returning users that already had a NextGen cookie. The error caused us to route to the beginning. Fixed that error by removing the default true in the navigateToHeritageFunnel and changing all calls to pass a true or false.
527 lines
26 KiB
JavaScript
527 lines
26 KiB
JavaScript
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
|
import { storeMutations } from "@/constants/store-mutations.js";
|
|
import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
|
|
import { storeActions } from "@/constants/store-actions";
|
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
|
import baseMixin from "@/mixins/base-mixin.js";
|
|
import store from "@/store";
|
|
|
|
export default {
|
|
methods: {
|
|
hasPartQuestions(partsOrQuestions) {
|
|
return partsOrQuestions?.some((pq) => pq.partQuestions?.length > 0);
|
|
},
|
|
hasGlassLocationWithMultipleParts(partsOrQuestions) {
|
|
return partsOrQuestions?.some((pq) => pq.parts?.length > 1);
|
|
},
|
|
hasChildPartQuestions(partsOrQuestions) {
|
|
return partsOrQuestions?.some((pq) => {
|
|
return pq.parts?.some((part) => part.childPartQuestions?.length > 0);
|
|
});
|
|
},
|
|
hasCapabilityQuestions(partsOrQuestions) {
|
|
return partsOrQuestions?.some((pq) => {
|
|
return pq.parts?.some((part) => part.requiresCapabilityQuestions === true);
|
|
});
|
|
},
|
|
|
|
// method to only include keys listed for lineItems.glassParts in
|
|
// https://safelite.atlassian.net/wiki/spaces/DC/pages/17137665/Catalog+Front-End+State#glassParts
|
|
reducedGlassPartsArray(glassParts) {
|
|
const reducedGlassParts = [];
|
|
glassParts.forEach((glass) => {
|
|
if (Array.isArray(glass.parts) && glass.parts.length === 1) {
|
|
const singlePart = glass.parts[0];
|
|
reducedGlassParts.push({
|
|
partNumber: singlePart.partNumber,
|
|
description: singlePart.description,
|
|
color: singlePart.color,
|
|
partType: singlePart.partType,
|
|
canSafeliteRecalibrate: singlePart.canSafeliteRecalibrate,
|
|
requiresRecalibration: singlePart.requiresRecalibration,
|
|
requiresCapabilityQuestions: singlePart.requiresCapabilityQuestions,
|
|
recalibrationType: singlePart.recalibrationType,
|
|
childParts: singlePart.childParts,
|
|
kitPrice: singlePart.kitPrice,
|
|
sellingPrice: singlePart.sellingPrice,
|
|
laborAmount: singlePart.laborAmount,
|
|
});
|
|
}
|
|
});
|
|
return reducedGlassParts;
|
|
},
|
|
comparePageIndices(currentPage, fmgPage) {
|
|
const orderedVehicleQuestionPages = [
|
|
fmgPageValues.PART_QUESTIONS,
|
|
fmgPageValues.VEHICLE_PARTS,
|
|
fmgPageValues.MOLDING_QUESTIONS,
|
|
fmgPageValues.CAPABILITY_QUESTIONS,
|
|
fmgPageValues.QUOTE,
|
|
];
|
|
|
|
return (
|
|
orderedVehicleQuestionPages.indexOf(currentPage) -
|
|
orderedVehicleQuestionPages.indexOf(fmgPage)
|
|
);
|
|
},
|
|
currentPageComesBeforePage(currentPage = this.$route.query.fmgPage, fmgPage) {
|
|
return this.comparePageIndices(currentPage, fmgPage) < 0;
|
|
},
|
|
currentPageComesAfterPage(currentPage = this.$route.query.fmgPage, fmgPage) {
|
|
return this.comparePageIndices(currentPage, fmgPage) > 0;
|
|
},
|
|
setupInitialData(glass, index, alreadyAnsweredQuestions, vm) {
|
|
const self = vm ?? this;
|
|
|
|
// clear answerData if no questions are already answered
|
|
if (!alreadyAnsweredQuestions) {
|
|
glass.answerData = null;
|
|
}
|
|
|
|
alreadyAnsweredQuestions?.forEach((answeredGlass) => {
|
|
// if answeredGlass lacks any of these properties then exit
|
|
if (
|
|
!answeredGlass.glassLocation ||
|
|
!answeredGlass.glassName ||
|
|
!answeredGlass.answeredQuestions ||
|
|
(!answeredGlass.result && !answeredGlass.partNum)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// test if glass parts match
|
|
if (
|
|
glass.glassLocation === answeredGlass.glassLocation &&
|
|
glass.glassName === answeredGlass.glassName
|
|
) {
|
|
let answerString = "";
|
|
|
|
// loop through answeredQuestions for matches
|
|
answeredGlass.answeredQuestions.forEach((answeredQuestion) => {
|
|
if (!answeredQuestion.questionNum || !answeredQuestion.selectedAnswerText) {
|
|
return;
|
|
}
|
|
// determine which answer was previously chosen
|
|
const chosenAns = glass.questions[
|
|
answeredQuestion.questionNum - 1
|
|
].answers.find((a) => {
|
|
return (
|
|
a.answerText.toUpperCase() ===
|
|
answeredQuestion.selectedAnswerText.toUpperCase()
|
|
);
|
|
});
|
|
// set the answerString to use for answerSelected
|
|
if (chosenAns.nextQuestionSequence) {
|
|
answerString = `${answeredQuestion.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`;
|
|
} else {
|
|
answerString = `${answeredQuestion.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}`;
|
|
}
|
|
|
|
// mark this question as answered (question-chain will read this)
|
|
glass.questions[answeredQuestion.questionNum - 1].answerSelected =
|
|
answerString;
|
|
// mark this question as suppressed if needed (question-chain uses this)
|
|
if (answeredQuestion.suppressThisQuestion) {
|
|
glass.questions[
|
|
answeredQuestion.questionNum - 1
|
|
].suppressThisQuestion = true;
|
|
}
|
|
});
|
|
|
|
// advance the currentGlassIndex
|
|
self.currentGlassIndex = index;
|
|
|
|
const answerResult = answeredGlass.partNum
|
|
? answeredGlass.partNum
|
|
: answeredGlass.result;
|
|
// the above logic for answerResult covers all 3 ___-questions page scenarios
|
|
|
|
// add answerData to current glass
|
|
glass.answerData = {
|
|
answerResult: answerResult,
|
|
answeredQuestions: answeredGlass.answeredQuestions,
|
|
};
|
|
}
|
|
});
|
|
|
|
return glass;
|
|
},
|
|
handleCompletedQuestionChainAnswers(answer, glassKey, vm) {
|
|
// only runs when all questions in a question-chain have been answered
|
|
// when selectedAnswers updates, user has completed this part's question chain and has a final answer
|
|
// (does not get run for each invididual question's answer, only when
|
|
// all relevant questions for the current part have been answered)
|
|
|
|
/* answer example format:
|
|
{
|
|
"answerResult": "DD11132",
|
|
"answeredQuestions": [
|
|
{
|
|
"questionText": "Is your Grand Cherokee the Laredo model?",
|
|
"selectedAnswerText": "Yes",
|
|
"questionNum": 1,
|
|
}
|
|
],
|
|
"index": 0
|
|
}
|
|
*/
|
|
|
|
const self = vm ?? this;
|
|
|
|
// clear out any preloaded answers
|
|
self.selectedAnswers = {};
|
|
|
|
// loop through every answered question on the currently answered glass part
|
|
answer.answeredQuestions?.forEach((answeredQuestion, answeredQuestionIndex) => {
|
|
/* answeredQuestion example format:
|
|
{
|
|
"questionText": "Is your Grand Cherokee the Laredo model?",
|
|
"selectedAnswer": "1|nextQuestion|3|Yes",
|
|
"selectedAnswerText": "Yes",
|
|
"questionNum": 1,
|
|
}
|
|
*/
|
|
|
|
const answeredQuestionText = answeredQuestion.questionText.toUpperCase();
|
|
const answeredQuestionAnswer = answeredQuestion.selectedAnswer.toUpperCase();
|
|
const answeredQuestionAnswerText =
|
|
answeredQuestion.selectedAnswerText.toUpperCase();
|
|
const answeredQuestionNum = answeredQuestion.questionNum;
|
|
|
|
// HANDLE DUPLICATE QUESTIONS
|
|
|
|
// loop through all glass data
|
|
self.questionsData.forEach((glass, glassIndex) => {
|
|
if (glassIndex === answer.index) {
|
|
glass.questions.forEach((question, questionIndex) => {
|
|
// clear out any previously set answers on first pass with first answered question
|
|
if (answeredQuestionIndex === 0) question.answerSelected = null;
|
|
|
|
if (answeredQuestionNum - 1 === questionIndex) {
|
|
// on the right question
|
|
question.answerSelected = answeredQuestionAnswer;
|
|
}
|
|
});
|
|
}
|
|
|
|
// limit duplicate search to glass pieces that are in or follow after the currently being answered glass piece
|
|
if (glassIndex > answer.index) {
|
|
let indexToSuppressTo;
|
|
// reset this glass piece, in case user is changing their previous answers
|
|
glass.answerData = null;
|
|
glass.isSuppressedPart = null;
|
|
|
|
// loop through this glass piece's questions, looking for a questionText match
|
|
glass.questions.forEach((question, questionIndex) => {
|
|
// clear out any previously set answers on first pass with first answer
|
|
if (answeredQuestionIndex === 0) question.answerSelected = null;
|
|
|
|
// clear or set suppressThisQuestion property for each question
|
|
if (indexToSuppressTo && questionIndex + 1 < indexToSuppressTo) {
|
|
question.suppressThisQuestion = true;
|
|
} else {
|
|
question.suppressThisQuestion = null;
|
|
}
|
|
|
|
// handle duplicate questions
|
|
if (question.questionText.toUpperCase() === answeredQuestionText) {
|
|
let matchedAnswer;
|
|
|
|
// handle matching answer in duplicated question
|
|
question.answers.forEach((ans) => {
|
|
if (
|
|
ans.answerText.toUpperCase() === answeredQuestionAnswerText
|
|
) {
|
|
matchedAnswer = ans;
|
|
ans.selected = true;
|
|
} else {
|
|
ans.selected = null;
|
|
}
|
|
});
|
|
|
|
// handle duplicate's nextQuestion logic on other related questions
|
|
if (matchedAnswer.nextQuestionSequence) {
|
|
// clear any suppression on the nextQuestion
|
|
glass.questions[
|
|
matchedAnswer.nextQuestionSequence - 1
|
|
].suppressThisQuestion = null;
|
|
// if the duplicate is 1ST question in array, set indexToSuppressTo
|
|
if (questionIndex === 0) {
|
|
if (
|
|
!indexToSuppressTo ||
|
|
matchedAnswer.nextQuestionSequence < indexToSuppressTo
|
|
) {
|
|
indexToSuppressTo = matchedAnswer.nextQuestionSequence;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update answers in this glass piece's questions with duplication logic modifications
|
|
glass.questions.forEach((q) => {
|
|
q.answers.forEach((a) => {
|
|
// revert any previously set nextQuestion logic modifications
|
|
if (
|
|
a.originalNextQuestionSequence ===
|
|
question.questionSequence
|
|
) {
|
|
// restore original nextQuestionSequence
|
|
a.nextQuestionSequence = a.originalNextQuestionSequence;
|
|
self.originalNextQuestionSequence = null;
|
|
// restore original answerResult
|
|
if (a.originalAnswerResult) {
|
|
a.answerResult = a.originalAnswerResult;
|
|
a.originalAnswerResult = null;
|
|
}
|
|
}
|
|
// modify logic on any related questions
|
|
if (a.nextQuestionSequence === question.questionSequence) {
|
|
// update either nextQuestionSequence or answerResult
|
|
|
|
// update questions that lead to duplicated question
|
|
if (matchedAnswer.nextQuestionSequence) {
|
|
a.originalNextQuestionSequence =
|
|
a.nextQuestionSequence;
|
|
a.nextQuestionSequence =
|
|
matchedAnswer.nextQuestionSequence;
|
|
} else {
|
|
a.originalNextQuestionSequence =
|
|
a.nextQuestionSequence;
|
|
a.nextQuestionSequence = null;
|
|
a.originalAnswerResult =
|
|
a.originalAnswerResult || a.answerResult;
|
|
a.answerResult = matchedAnswer.answerResult;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// suppress current question
|
|
question.suppressThisQuestion = true;
|
|
|
|
// are there any questions left that are not suppressed?
|
|
const remainingQuestions = glass.questions.filter((q) => {
|
|
return !q.suppressThisQuestion;
|
|
});
|
|
|
|
if (remainingQuestions.length < 1) {
|
|
// this is the final answer for this glass piece
|
|
|
|
// mark this glass piece as completely answered by adding answerData
|
|
const answeredQuestionObj = {
|
|
questionText: question.questionText,
|
|
selectedAnswerText: matchedAnswer.answerText,
|
|
questionNum: question.questionSequence,
|
|
suppressThisQuestion: question.suppressThisQuestion,
|
|
};
|
|
// set the answerData (used as indicator that it has been already answered)
|
|
glass.answerData = {
|
|
answerResult: matchedAnswer.nextQuestionSequence
|
|
? matchedAnswer.nextQuestionSequence
|
|
: matchedAnswer.answerResult,
|
|
answeredQuestions: [answeredQuestionObj],
|
|
};
|
|
|
|
// suppress this glass piece because it has an answer
|
|
glass.isSuppressedPart = true;
|
|
}
|
|
}
|
|
}); // DONE looping through glass.questions
|
|
|
|
// Update key to force re-render of glass piece with duplicate question in case user changes previous related answer in the chain
|
|
self.questionsData[glassIndex].key =
|
|
self.questionsData[glassIndex].key + Date.now().toString();
|
|
}
|
|
});
|
|
});
|
|
|
|
// set final answer data for the current answered glass part
|
|
self.questionsData[answer.index].answerData = {
|
|
answerResult: answer.answerResult,
|
|
answeredQuestions: answer.answeredQuestions,
|
|
};
|
|
|
|
// this part has been fully answered, so advance to next part's question chain
|
|
for (let i = answer.index + 1; i < this.questionsData.length; i++) {
|
|
// if this part has not yet been fully answered, then make it the current part
|
|
if (!this.questionsData[i].answerData?.answerResult) {
|
|
this.currentGlassIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
// Can't use `this` because navigateForward is also called from vin-pages-mixin
|
|
async navigateForward(partsOrQuestions, vm) {
|
|
const self = vm ?? this;
|
|
const currentPage = self.$route.query.fmgPage;
|
|
|
|
const hasPartQuestions = this.hasPartQuestions(partsOrQuestions);
|
|
const hasGlassLocationWithMultipleParts =
|
|
this.hasGlassLocationWithMultipleParts(partsOrQuestions);
|
|
const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions);
|
|
const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions);
|
|
|
|
if (
|
|
hasPartQuestions &&
|
|
this.currentPageComesBeforePage(currentPage, fmgPageValues.PART_QUESTIONS)
|
|
) {
|
|
self.$router.navigateWithSaving(
|
|
self.navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
|
|
self.$route,
|
|
{},
|
|
{},
|
|
{ partsOrQuestions: partsOrQuestions }
|
|
);
|
|
} else if (
|
|
hasGlassLocationWithMultipleParts &&
|
|
this.currentPageComesBeforePage(currentPage, fmgPageValues.VEHICLE_PARTS)
|
|
) {
|
|
// if multiple parts on any glass
|
|
// go to vehicle-parts page and pass the partsData
|
|
self.$router.navigateWithSaving(
|
|
self.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
|
|
self.$route,
|
|
{},
|
|
{},
|
|
{ partsOrQuestions: partsOrQuestions }
|
|
);
|
|
} else if (
|
|
hasChildPartQuestions &&
|
|
this.currentPageComesBeforePage(currentPage, fmgPageValues.MOLDING_QUESTIONS)
|
|
) {
|
|
// if any childpart questions
|
|
// go to molding-questions page and pass the partsData
|
|
self.$router.navigateWithSaving(
|
|
self.navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
|
|
self.$route,
|
|
{},
|
|
{},
|
|
{ partsOrQuestions: partsOrQuestions }
|
|
);
|
|
} else if (
|
|
hasCapabilityQuestions &&
|
|
this.currentPageComesBeforePage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)
|
|
) {
|
|
// if has capability questions
|
|
// go to capability-questions page and pass the partsData
|
|
|
|
// mimic part-questions page data for consistency
|
|
for (let partOrQuestion of partsOrQuestions) {
|
|
if (this.hasCapabilityQuestions([partOrQuestion])) {
|
|
let capabilityQuestionsForGlassLocation = (
|
|
await baseMixin.methods.dispatchStoreAction(
|
|
storeActions.GET_CAPABILITY_QUESTIONS,
|
|
{
|
|
carId: store.getters.vehicle.carId,
|
|
partNumber: partOrQuestion.parts[0].partNumber,
|
|
}
|
|
)
|
|
).data;
|
|
|
|
capabilityQuestionsForGlassLocation.forEach((question) => {
|
|
question.answers = question.answers.map((answer) => {
|
|
return {
|
|
...answer,
|
|
answerResult: answer.answerResult1,
|
|
};
|
|
});
|
|
});
|
|
|
|
partOrQuestion.capabilityQuestions = capabilityQuestionsForGlassLocation;
|
|
}
|
|
}
|
|
|
|
self.$router.navigateWithSaving(
|
|
self.navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
|
|
self.$route,
|
|
{},
|
|
{},
|
|
{ partsOrQuestions }
|
|
);
|
|
} else {
|
|
// if single parts only
|
|
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
|
|
|
|
// save to store lineItems.glassParts
|
|
self.dispatchStoreAction(storeActions.SAVE_GLASS_PARTS, collectedGlassParts, false);
|
|
|
|
const payment = store.getters.payment;
|
|
|
|
if (store.getters.order.referralNumber?.length === 6) {
|
|
navigateToHeritageFunnel({
|
|
shouldSaveSession: true,
|
|
loadingModal: self.$refs.loadingModal,
|
|
});
|
|
} else if (payment.isInsurance && payment.insuranceCoverage.isVerified) {
|
|
navigateToHeritageFunnel({
|
|
shouldSaveSession: true,
|
|
loadingModal: self.$refs.loadingModal,
|
|
});
|
|
} else {
|
|
self.$router.navigateWithSaving(
|
|
self.navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
|
|
self.$route
|
|
);
|
|
}
|
|
}
|
|
},
|
|
// Can't use `this` because navigateForward is also called from quote
|
|
async navigateBack(vm) {
|
|
const self = vm ?? this;
|
|
const currentPage = self.$route.query.fmgPage;
|
|
const pageDataCapabilityQuestions = self.$store.getters.pageData(
|
|
fmgPageValues.CAPABILITY_QUESTIONS
|
|
);
|
|
const pageDataMoldingQuestions = self.$store.getters.pageData(
|
|
fmgPageValues.MOLDING_QUESTIONS
|
|
);
|
|
const pageDataVehicleParts = self.$store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
|
const pageDataPartQuestions = self.$store.getters.pageData(
|
|
fmgPageValues.PART_QUESTIONS
|
|
);
|
|
|
|
const allPartsOrQuestions = [
|
|
...(pageDataCapabilityQuestions?.partsOrQuestions || []),
|
|
...(pageDataMoldingQuestions?.partsOrQuestions || []),
|
|
...(pageDataVehicleParts?.partsOrQuestions || []),
|
|
...(pageDataPartQuestions?.partsOrQuestions || []),
|
|
];
|
|
|
|
const hasPartQuestions = this.hasPartQuestions(allPartsOrQuestions);
|
|
const hasGlassLocationWithMultipleParts =
|
|
this.hasGlassLocationWithMultipleParts(allPartsOrQuestions);
|
|
const hasChildPartQuestions = this.hasChildPartQuestions(allPartsOrQuestions);
|
|
const hasCapabilityQuestions = this.hasCapabilityQuestions(allPartsOrQuestions);
|
|
|
|
const vin = self.$store.getters.vehicle.vin;
|
|
let backNavigationScenario =
|
|
!vin || vin === ""
|
|
? navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS
|
|
: navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS;
|
|
|
|
if (
|
|
hasCapabilityQuestions &&
|
|
this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)
|
|
) {
|
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS;
|
|
} else if (
|
|
hasChildPartQuestions &&
|
|
this.currentPageComesAfterPage(currentPage, fmgPageValues.MOLDING_QUESTIONS)
|
|
) {
|
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS;
|
|
} else if (
|
|
hasGlassLocationWithMultipleParts &&
|
|
this.currentPageComesAfterPage(currentPage, fmgPageValues.VEHICLE_PARTS)
|
|
) {
|
|
backNavigationScenario =
|
|
navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE;
|
|
} else if (
|
|
hasPartQuestions &&
|
|
this.currentPageComesAfterPage(currentPage, fmgPageValues.PART_QUESTIONS)
|
|
) {
|
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS;
|
|
}
|
|
|
|
self.$router.navigateWithoutSaving(backNavigationScenario, self.$route);
|
|
},
|
|
},
|
|
};
|