Merge pull request #805 from Safelite/feature/CSR-803-refactoring-2
Feature/CSR-803 refactoring 2
This commit is contained in:
commit
ca86cec56c
7 changed files with 1290 additions and 1177 deletions
|
|
@ -7,7 +7,7 @@
|
||||||
:class="q.questionSequence === currentQuestionNum && 'current-question'"
|
:class="q.questionSequence === currentQuestionNum && 'current-question'"
|
||||||
:questionText="q.questionText"
|
:questionText="q.questionText"
|
||||||
:answers="q.answers"
|
:answers="q.answers"
|
||||||
:groupName="`question-${glassIndex}-${q.questionSequence}`"
|
:groupName="`question-${index}-${q.questionSequence}`"
|
||||||
:modelValue="q.answerSelected"
|
:modelValue="q.answerSelected"
|
||||||
@update:modelValue="handleAnswer(q, $event)"
|
@update:modelValue="handleAnswer(q, $event)"
|
||||||
isRequired
|
isRequired
|
||||||
|
|
@ -31,8 +31,9 @@ export default {
|
||||||
props: {
|
props: {
|
||||||
questionData: Object,
|
questionData: Object,
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
modelValue: Array,
|
modelValue: Object,
|
||||||
glassIndex: Number,
|
index: Number,
|
||||||
|
answerKey: String,
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
// do a test validation check upon create to prevent out of sync / incorrect valid states
|
// do a test validation check upon create to prevent out of sync / incorrect valid states
|
||||||
|
|
@ -65,7 +66,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
answerSelected: q.answerSelected || "",
|
answerSelected: q.answerSelected || "",
|
||||||
};
|
};
|
||||||
if (!q.suppressQuestion) {
|
if (!q.suppressThisQuestion) {
|
||||||
this.questions.push(question);
|
this.questions.push(question);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -81,7 +82,6 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleAnswer(question, returnedAnswer) {
|
handleAnswer(question, returnedAnswer) {
|
||||||
question.answerSelected = returnedAnswer;
|
|
||||||
/*
|
/*
|
||||||
returnedAnswer example format:
|
returnedAnswer example format:
|
||||||
{
|
{
|
||||||
|
|
@ -90,6 +90,8 @@ export default {
|
||||||
"buttonId": "Driver-Front-1-1|answer|DD11132|Yes"
|
"buttonId": "Driver-Front-1-1|answer|DD11132|Yes"
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
question.answerSelected = returnedAnswer;
|
||||||
const isQuestionChainComplete = this.getQuestionChainAnswerIfComplete(returnedAnswer);
|
const isQuestionChainComplete = this.getQuestionChainAnswerIfComplete(returnedAnswer);
|
||||||
|
|
||||||
if (isQuestionChainComplete) {
|
if (isQuestionChainComplete) {
|
||||||
|
|
@ -153,7 +155,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
answerResult: questionAnswer,
|
answerResult: questionAnswer,
|
||||||
answeredQuestions: answeredQuestions,
|
answeredQuestions: answeredQuestions,
|
||||||
glassIndex: this.glassIndex,
|
index: this.index,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
<template>
|
||||||
|
<div :class="`page-container-grouped-styles questions-page`">
|
||||||
|
<loadingModal ref="loadingModal" />
|
||||||
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
|
||||||
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
<div class="fade-on-route-transition sub-container make-tall">
|
||||||
|
<alert
|
||||||
|
ref="alertFewMoreQuestions"
|
||||||
|
cmsWidgetName="alertWidget"
|
||||||
|
class="my-5"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
:manualHeadline="alertFewMoreQuestionsHeader"
|
||||||
|
:manualCopy="alertFewMoreQuestionsCopy"
|
||||||
|
v-bind:isDismissible="false" />
|
||||||
|
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
|
||||||
|
<questionChain
|
||||||
|
ref="questionChain"
|
||||||
|
v-model="selectedAnswers[questionsDatum.answerKey]"
|
||||||
|
:questionData="questionsDatum.questions"
|
||||||
|
:index="i"
|
||||||
|
v-if="showThisQuestionChain(questionsDatum, i)"
|
||||||
|
:answerKey="questionsDatum.answerKey"
|
||||||
|
:validationRules="validationRules" />
|
||||||
|
</div>
|
||||||
|
<funnel-footer
|
||||||
|
ref="funnelFooter"
|
||||||
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
:isForwardActionDisabled="!isMetaValid"
|
||||||
|
@back-clicked="backButtonAction"
|
||||||
|
@ForwardClicked="handleForwardButtonAction" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Components
|
||||||
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
import questionChain from "@/common-components/question-chain/question-chain";
|
||||||
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
|
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "questions-page",
|
||||||
|
props: {
|
||||||
|
isMetaValid: Boolean,
|
||||||
|
alertFewMoreQuestionsHeader: String,
|
||||||
|
alertFewMoreQuestionsCopy: String,
|
||||||
|
questionsData: Array,
|
||||||
|
validationRules: String,
|
||||||
|
modelValue: Array,
|
||||||
|
index: Number,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedAnswers: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
this.$emit("update:modelValue", newValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showThisQuestionChain(glass, i) {
|
||||||
|
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) {
|
||||||
|
return false;
|
||||||
|
} // return false if no questions or if suppressed
|
||||||
|
return this.index === i || glass.answerData?.answerResult?.length > 0;
|
||||||
|
},
|
||||||
|
handleForwardButtonAction() {
|
||||||
|
this.$emit("forwardButtonAction");
|
||||||
|
},
|
||||||
|
handleBackButtonAction() {
|
||||||
|
this.$emit("backButtonAction");
|
||||||
|
},
|
||||||
|
showLoadingModal() {
|
||||||
|
this.$refs.loadingModal.showModal();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
funnelHeader,
|
||||||
|
vehicleBanner,
|
||||||
|
alert,
|
||||||
|
questionChain,
|
||||||
|
funnelSubHeader,
|
||||||
|
funnelFooter,
|
||||||
|
loadingModal,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.questions-page {
|
||||||
|
.question-text {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,61 +1,32 @@
|
||||||
<template>
|
<template>
|
||||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
||||||
<div class="page-container-grouped-styles capability-questions">
|
<questions-page-layout
|
||||||
<loadingModal ref="loadingModal" />
|
ref="questionsPageLayout"
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
:isMetaValid="meta.valid"
|
||||||
<vehicleBanner
|
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
|
||||||
:displayGenericVehicleImage="false" />
|
:questionsData="questionsData"
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
:validationRules="questions - required"
|
||||||
<div class="fade-on-route-transition sub-container make-tall">
|
v-model="selectedAnswers"
|
||||||
<alert
|
@forwardButtonAction="forwardButtonAction"
|
||||||
ref="alertFewMoreQuestions"
|
@backButtonAction="backButtonAction"
|
||||||
class="my-5"
|
:index="currentGlassIndex" />
|
||||||
alertClass="alert-warning"
|
|
||||||
:manualHeadline="AlertFewMoreQuestionsHeader"
|
|
||||||
:manualCopy="AlertFewMoreQuestionsCopy"
|
|
||||||
v-bind:isDismissible="false" />
|
|
||||||
<div v-for="(glass, i) in capabilityQuestionsData" :key="glass.key">
|
|
||||||
<questionChain
|
|
||||||
ref="questionChain"
|
|
||||||
v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
|
|
||||||
:questionData="glass.capabilityQuestions"
|
|
||||||
:glassIndex="i"
|
|
||||||
v-if="showThisQuestionChain(glass, i)"
|
|
||||||
validationRules="questions-required" />
|
|
||||||
</div>
|
|
||||||
<funnel-footer
|
|
||||||
ref="funnelFooter"
|
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
|
||||||
:isForwardActionDisabled="!meta.valid"
|
|
||||||
@back-clicked="backButtonAction"
|
|
||||||
@ForwardClicked="forwardButtonAction" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import questionsPageLayout from "@/common-components/questions-page-layout/questions-page-layout";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|
||||||
import alert from "@/ux-components/alert/alert";
|
|
||||||
import questionChain from "@/common-components/question-chain/question-chain";
|
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
|
||||||
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
|
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
@ -84,10 +55,9 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
capabilityQuestionsData: store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)
|
questionsData: [],
|
||||||
.partsOrQuestions,
|
|
||||||
selectedAnswers: {},
|
selectedAnswers: {},
|
||||||
currentQuestionChainIndex: 0,
|
currentGlassIndex: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -97,14 +67,6 @@ export default {
|
||||||
AlertFewMoreQuestionsCopy() {
|
AlertFewMoreQuestionsCopy() {
|
||||||
return this.getCmsContent("AdditionalPartsQuestionsAlert", "BodyText");
|
return this.getCmsContent("AdditionalPartsQuestionsAlert", "BodyText");
|
||||||
},
|
},
|
||||||
windshieldPart() {
|
|
||||||
return this.pageData.partsOrQuestions.find(
|
|
||||||
(x) => x.glassLocation === damageLocationsSelected.WINDSHIELD
|
|
||||||
);
|
|
||||||
},
|
|
||||||
windshieldPartInfo() {
|
|
||||||
return this.windshieldPart.parts[0];
|
|
||||||
},
|
|
||||||
pageData() {
|
pageData() {
|
||||||
return this.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
|
return this.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||||
},
|
},
|
||||||
|
|
@ -113,30 +75,32 @@ export default {
|
||||||
// are there alreadyAnsweredQuestions?
|
// are there alreadyAnsweredQuestions?
|
||||||
const alreadyAnsweredQuestions = store.getters.damage.capabilityQuestionAnswers;
|
const alreadyAnsweredQuestions = store.getters.damage.capabilityQuestionAnswers;
|
||||||
|
|
||||||
this.capabilityQuestionsData = this.pageData.partsOrQuestions
|
this.questionsData = this.pageData.partsOrQuestions
|
||||||
.filter((x) => x.capabilityQuestions)
|
.filter((x) => x.capabilityQuestions)
|
||||||
.map((glass, i) => {
|
.map((glass, index) => {
|
||||||
// NOTE: questions for property "questions" can differ between layouts
|
// NOTE: questions for property "questions" can differ between layouts
|
||||||
glass.questions = glass.capabilityQuestions;
|
glass.questions = glass.capabilityQuestions;
|
||||||
|
glass.answerKey = glass.glassLocation + "-" + glass.glassName;
|
||||||
// reset selectedAnswers for this glass
|
// reset selectedAnswers for this glass
|
||||||
this.selectedAnswers[glass.key] = [];
|
this.selectedAnswers[glass.answerKey] = [];
|
||||||
// pass in: glass, i, alreadyAnsweredQuestions
|
|
||||||
return this.setupInitialData(glass, i, alreadyAnsweredQuestions);
|
const updatedGlass = this.setupInitialData(glass, index, alreadyAnsweredQuestions);
|
||||||
|
|
||||||
|
// Set up watch for each set of glass questions
|
||||||
|
this.$watch(
|
||||||
|
"selectedAnswers." + glass.answerKey,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue && Object.keys(newValue).length > 0) {
|
||||||
|
this.handleAnswerUpdates(newValue, glass.answerKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return updatedGlass;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showThisQuestionChain(glass, i) {
|
|
||||||
if (
|
|
||||||
!glass.capabilityQuestions ||
|
|
||||||
glass.capabilityQuestions?.length < 1 ||
|
|
||||||
glass.isSuppressedPart
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
} // return false if no capabilityQuestions or if suppressed
|
|
||||||
return (
|
|
||||||
this.currentQuestionChainIndex === i || glass.answerData?.answerResult?.length > 0
|
|
||||||
);
|
|
||||||
},
|
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
const capabilityQuestionsPageData = store.getters.pageData(
|
const capabilityQuestionsPageData = store.getters.pageData(
|
||||||
fmgPageValues.CAPABILITY_QUESTIONS
|
fmgPageValues.CAPABILITY_QUESTIONS
|
||||||
|
|
@ -146,10 +110,17 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
const capabilityQuestionsAnswersArray = this.capabilityQuestionsData.map((glass) => {
|
const questionAnswersArray = this.questionsData.map((glass) => {
|
||||||
const selectedAnswerResult2 = this.getCorrespondingAnswerResult2(
|
// get answerResult2 of returned answer
|
||||||
glass.answerData.answerResult
|
let selectedAnswerResult2;
|
||||||
);
|
glass.questions.forEach((q) => {
|
||||||
|
const idx = q.answers.findIndex(
|
||||||
|
(a) => a.answerResult === glass.answerData.answerResult
|
||||||
|
);
|
||||||
|
if (idx !== -1) {
|
||||||
|
selectedAnswerResult2 = q.answers[idx].answerResult2;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
glassLocation: glass.glassLocation,
|
glassLocation: glass.glassLocation,
|
||||||
|
|
@ -163,361 +134,39 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
// clear out answerData for future page loads; must occur prior to store save
|
// clear out answerData for future page loads; must occur prior to store save
|
||||||
this.capabilityQuestionsData.forEach((glass) => {
|
this.questionsData.forEach((glass) => {
|
||||||
glass.answerData = {};
|
glass.answerData = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
// save to vuex store as order.damage.capabilityQuestionAnswers (array)
|
// save to vuex store as order.damage.capabilityQuestionAnswers (array)
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS,
|
this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS,
|
||||||
capabilityQuestionsAnswersArray,
|
questionAnswersArray,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
// get parts from the capabilityQuestionAnswers
|
// get parts from the capabilityQuestionAnswers
|
||||||
let partsOrQuestions = this.pageData.partsOrQuestions;
|
const partsOrQuestions = this.pageData.partsOrQuestions;
|
||||||
for (let answer of capabilityQuestionsAnswersArray) {
|
for (let answer of questionAnswersArray) {
|
||||||
const correspondingPart = partsOrQuestions.find(
|
|
||||||
(partOrQuestion) => partOrQuestion.glassLocation === answer.glassLocation
|
|
||||||
);
|
|
||||||
const partFromCapabilityQuestionAnswer = (
|
const partFromCapabilityQuestionAnswer = (
|
||||||
await this.dispatchStoreAction(
|
await this.dispatchStoreAction(
|
||||||
storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER,
|
this.storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER,
|
||||||
answer.glassLocation,
|
answer.glassLocation,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
|
|
||||||
partsOrQuestions.find(
|
partsOrQuestions.find(
|
||||||
(partOrQuestion) => partOrQuestion.location === answer.location
|
(partOrQuestion) => partOrQuestion.glassLocation === answer.glassLocation
|
||||||
).parts = partFromCapabilityQuestionAnswer;
|
).parts = partFromCapabilityQuestionAnswer;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.navigateForward(partsOrQuestions);
|
this.navigateForward(partsOrQuestions);
|
||||||
},
|
},
|
||||||
handleAnswerUpdates(answer) {
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"glassIndex": 0
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// collect a list of the answered questions' numbers, needed later below
|
|
||||||
const answeredQuestionIndexes = [];
|
|
||||||
const foundDuplicateQuestions = [];
|
|
||||||
|
|
||||||
// if user has answered a question differently than anything that was preloaded,
|
|
||||||
// we need to clear out any preloaded answers
|
|
||||||
this.selectedAnswers = {};
|
|
||||||
|
|
||||||
// loop through every answered question on the currently answered glass part
|
|
||||||
answer.answeredQuestions?.forEach((aq) => {
|
|
||||||
// keep track of this question number
|
|
||||||
answeredQuestionIndexes.push(aq.questionNum);
|
|
||||||
|
|
||||||
const answeredQuestionText = aq.questionText.toUpperCase();
|
|
||||||
const answeredQuestionAnswer = aq.selectedAnswerText.toUpperCase();
|
|
||||||
|
|
||||||
// HANDLE DUPLICATE QUESTIONS
|
|
||||||
|
|
||||||
// loop through all glass parts data
|
|
||||||
this.capabilityQuestionsData.forEach((glass, gpIndex) => {
|
|
||||||
// only look for duplicates forward... to parts that follow after the currently being answered part
|
|
||||||
if (gpIndex > answer.glass) {
|
|
||||||
let suppressUntil;
|
|
||||||
// reset this glass part, in case user is changing their previous answers
|
|
||||||
glass.answerData = null;
|
|
||||||
glass.isSuppressedPart = null;
|
|
||||||
|
|
||||||
// loop through this glass part's part questions, looking for a questionText match
|
|
||||||
glass.capabilityQuestions.forEach((pq, pqIndex) => {
|
|
||||||
// clear out any previously set answers
|
|
||||||
pq.answerSelected = null;
|
|
||||||
|
|
||||||
// clear or set suppressQuestion property for each question
|
|
||||||
if (suppressUntil) {
|
|
||||||
// if suppressUntil has been set, then suppress this question if before it
|
|
||||||
if (pqIndex + 1 < suppressUntil) {
|
|
||||||
pq.suppressQuestion = true;
|
|
||||||
} else {
|
|
||||||
pq.suppressQuestion = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pq.suppressQuestion = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if these match then we have a duplicate question
|
|
||||||
if (pq.questionText.toUpperCase() === answeredQuestionText) {
|
|
||||||
const thisAnsweredCapabilityQuestion =
|
|
||||||
glass.capabilityQuestions[pqIndex];
|
|
||||||
let matchedAnswer;
|
|
||||||
let rejectedAnswers = []; // set as an array, in case we ever have questions with more than 2 answers...
|
|
||||||
|
|
||||||
// which one of this capabilityQuestions' answers matches our answer?
|
|
||||||
pq.answers.forEach((ans) => {
|
|
||||||
if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
|
|
||||||
matchedAnswer = ans;
|
|
||||||
ans.selected = true;
|
|
||||||
} else {
|
|
||||||
rejectedAnswers.push(ans);
|
|
||||||
ans.selected = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the key to re-render this part's question-chain component
|
|
||||||
this.capabilityQuestionsData[gpIndex].key =
|
|
||||||
this.capabilityQuestionsData[gpIndex].glassLocation +
|
|
||||||
this.capabilityQuestionsData[gpIndex].glassName +
|
|
||||||
Date.now().toString();
|
|
||||||
|
|
||||||
// handle suppressing downstream in this question chain
|
|
||||||
|
|
||||||
if (matchedAnswer.nextQuestionSequence) {
|
|
||||||
// ensure that the question that the accepted answer has set to be next is NOT suppressed
|
|
||||||
glass.capabilityQuestions[
|
|
||||||
matchedAnswer.nextQuestionSequence - 1
|
|
||||||
].suppressQuestion = null;
|
|
||||||
// if the duplicate is the 1ST question, then set suppressUntil var to lowest nextQuestion number
|
|
||||||
if (pqIndex === 0) {
|
|
||||||
if (!suppressUntil) {
|
|
||||||
suppressUntil = matchedAnswer.nextQuestionSequence;
|
|
||||||
}
|
|
||||||
if (matchedAnswer.nextQuestionSequence < suppressUntil) {
|
|
||||||
suppressUntil = matchedAnswer.nextQuestionSequence;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle suppressing upstream in this question chain
|
|
||||||
glass.capabilityQuestions.forEach((q) => {
|
|
||||||
q.answers.forEach((thisAns) => {
|
|
||||||
// restore any of the answers that formerly led to the duplicated question
|
|
||||||
if (
|
|
||||||
thisAns.originalNextQuestionSequence ===
|
|
||||||
pq.questionSequence
|
|
||||||
) {
|
|
||||||
// restore original nextQuestionSequence
|
|
||||||
thisAns.nextQuestionSequence =
|
|
||||||
thisAns.originalNextQuestionSequence;
|
|
||||||
this.originalNextQuestionSequence = null;
|
|
||||||
// restore original answerResult
|
|
||||||
if (thisAns.originalAnswerResult) {
|
|
||||||
thisAns.answerResult = thisAns.originalAnswerResult;
|
|
||||||
thisAns.originalAnswerResult = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// search for any answers that lead to the duplicated question
|
|
||||||
if (thisAns.nextQuestionSequence === pq.questionSequence) {
|
|
||||||
// update either the nextQuestionSequence or the answerResult
|
|
||||||
if (matchedAnswer.nextQuestionSequence) {
|
|
||||||
thisAns.originalNextQuestionSequence =
|
|
||||||
thisAns.nextQuestionSequence;
|
|
||||||
thisAns.nextQuestionSequence =
|
|
||||||
matchedAnswer.nextQuestionSequence;
|
|
||||||
} else {
|
|
||||||
thisAns.originalNextQuestionSequence =
|
|
||||||
thisAns.nextQuestionSequence;
|
|
||||||
thisAns.nextQuestionSequence = null;
|
|
||||||
thisAns.originalAnswerResult =
|
|
||||||
thisAns.originalAnswerResult ||
|
|
||||||
thisAns.answerResult;
|
|
||||||
thisAns.answerResult = matchedAnswer.answerResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// suppress current question
|
|
||||||
thisAnsweredCapabilityQuestion.suppressQuestion = true;
|
|
||||||
|
|
||||||
const thisGlassPart = "glass" + gpIndex;
|
|
||||||
if (foundDuplicateQuestions[thisGlassPart]) {
|
|
||||||
if (
|
|
||||||
!foundDuplicateQuestions[thisGlassPart].includes(
|
|
||||||
thisAnsweredCapabilityQuestion.questionSequence
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
foundDuplicateQuestions[thisGlassPart].push(
|
|
||||||
thisAnsweredCapabilityQuestion.questionSequence
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foundDuplicateQuestions[thisGlassPart] = [
|
|
||||||
thisAnsweredCapabilityQuestion.questionSequence,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// are there any questions left that are not suppressed?
|
|
||||||
const remainingQuestions = glass.capabilityQuestions.filter((q) => {
|
|
||||||
return !q.suppressQuestion;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (remainingQuestions.length < 1) {
|
|
||||||
// this is the final answer for this glass part
|
|
||||||
|
|
||||||
// mark this part as completely answered by adding answerData
|
|
||||||
const answeredQuestionObj = {
|
|
||||||
questionText: pq.questionText,
|
|
||||||
selectedAnswerText: matchedAnswer.answerText,
|
|
||||||
questionNum: pq.questionSequence,
|
|
||||||
suppressQuestion: pq.suppressQuestion,
|
|
||||||
};
|
|
||||||
// set the answerData as 'already answered'
|
|
||||||
glass.answerData = {
|
|
||||||
answerResult: matchedAnswer.nextQuestionSequence
|
|
||||||
? matchedAnswer.nextQuestionSequence
|
|
||||||
: matchedAnswer.answerResult,
|
|
||||||
answeredQuestions: [answeredQuestionObj],
|
|
||||||
};
|
|
||||||
|
|
||||||
// suppress this glass because it has an answer
|
|
||||||
glass.isSuppressedPart = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the key to re-render this part's question-chain component
|
|
||||||
this.capabilityQuestionsData[gpIndex].key =
|
|
||||||
this.capabilityQuestionsData[gpIndex].glassLocation +
|
|
||||||
this.capabilityQuestionsData[gpIndex].glassName +
|
|
||||||
Date.now().toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// DETERMINE ANSWERED QUESTIONS LIST FOR THIS GLASS PART
|
|
||||||
|
|
||||||
// look through all (this part's) part questions for any duplicates that were suppressed;
|
|
||||||
// add them to the list of answered questions if found
|
|
||||||
|
|
||||||
// EX answeredQuestionIndexes: [1,5,11,13]
|
|
||||||
|
|
||||||
// EX foundDuplicateQuestions = {
|
|
||||||
// "glassPart1": [1],
|
|
||||||
// "glassPart2": [7, 10]
|
|
||||||
// };
|
|
||||||
|
|
||||||
const thisPartsDupes = foundDuplicateQuestions["glass" + answer.glassIndex];
|
|
||||||
const completeAnsweredQuestions = answer.answeredQuestions
|
|
||||||
? [...answer.answeredQuestions]
|
|
||||||
: [];
|
|
||||||
const glassPartAnswered = this.capabilityQuestionsData[answer.glassIndex];
|
|
||||||
|
|
||||||
thisPartsDupes?.forEach((dupe) => {
|
|
||||||
// dupe is a single integer
|
|
||||||
const dupeQuestion = glassPartAnswered.capabilityQuestions[dupe - 1];
|
|
||||||
const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);
|
|
||||||
|
|
||||||
glassPartAnswered.capabilityQuestions.forEach((q) => {
|
|
||||||
let includeThisDupeInAnsweredQuestions = false;
|
|
||||||
|
|
||||||
// did one of the answers of this question point to the duplicated question?
|
|
||||||
q.answers.forEach((a) => {
|
|
||||||
if (
|
|
||||||
dupe === a.originalNextQuestionSequence &&
|
|
||||||
answeredQuestionIndexes.includes(q.questionSequence) &&
|
|
||||||
a.answerText.toUpperCase() ===
|
|
||||||
dupeQuestionAnswer.answerText.toUpperCase()
|
|
||||||
) {
|
|
||||||
includeThisDupeInAnsweredQuestions = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// is this q.questionSequence listed as the duplicated question's nextQuestionSequence?
|
|
||||||
if (
|
|
||||||
q.questionSequence === dupeQuestionAnswer.nextQuestionSequence &&
|
|
||||||
answeredQuestionIndexes.includes(q.questionSequence)
|
|
||||||
) {
|
|
||||||
includeThisDupeInAnsweredQuestions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeThisDupeInAnsweredQuestions) {
|
|
||||||
completeAnsweredQuestions.push({
|
|
||||||
questionNum: dupeQuestion.questionSequence,
|
|
||||||
questionText: dupeQuestion.questionText,
|
|
||||||
selectedAnswerText: dupeQuestionAnswer.answerText,
|
|
||||||
suppressQuestion: dupeQuestion.suppressQuestion,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// make sure there are no duplicated dupes in the list...
|
|
||||||
const foundInCompleteAnsweredQuestions = new Set();
|
|
||||||
let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter((el) => {
|
|
||||||
const duplicate = foundInCompleteAnsweredQuestions.has(el.questionText);
|
|
||||||
foundInCompleteAnsweredQuestions.add(el.questionText);
|
|
||||||
return !duplicate;
|
|
||||||
});
|
|
||||||
filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort(
|
|
||||||
(a, b) => a.questionNum - b.questionNum
|
|
||||||
);
|
|
||||||
|
|
||||||
// set final answer data for the current answered glass part
|
|
||||||
glassPartAnswered.answerData = {
|
|
||||||
answerResult: answer.answerResult,
|
|
||||||
answeredQuestions: filteredCompleteAnsweredQuestions,
|
|
||||||
};
|
|
||||||
|
|
||||||
// this part has been fully answered, so advance to next part's question chain
|
|
||||||
for (let i = answer.glassIndex + 1; i < this.capabilityQuestionsData.length; i++) {
|
|
||||||
// if this part has not yet been fully answered, then make it the current part
|
|
||||||
if (!this.capabilityQuestionsData[i].answerData?.answerResult) {
|
|
||||||
this.currentQuestionChainIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// TODO I'm sure there's a better/simplier way to do this that I'm missing but my brain's fried
|
|
||||||
// Find the part that has some answer that has an answerResult matching the selected answerResult, then get the corresponding answerResult2
|
|
||||||
getCorrespondingAnswerResult2(answerResult1) {
|
|
||||||
return this.capabilityQuestionsData
|
|
||||||
.find((part) =>
|
|
||||||
part.capabilityQuestions.some((question) =>
|
|
||||||
question.answers.some((answer) => answer.answerResult == answerResult1)
|
|
||||||
)
|
|
||||||
) // found part
|
|
||||||
.capabilityQuestions.find((question) =>
|
|
||||||
question.answers.some((answer) => answer.answerResult == answerResult1)
|
|
||||||
) // found question
|
|
||||||
.answers.find((answer) => answer.answerResult == answerResult1).answerResult2;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
|
||||||
vehicleBanner,
|
|
||||||
alert,
|
|
||||||
questionChain,
|
|
||||||
funnelSubHeader,
|
|
||||||
funnelFooter,
|
|
||||||
loadingModal,
|
|
||||||
Form,
|
Form,
|
||||||
|
questionsPageLayout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.capability-questions {
|
|
||||||
.question-text {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
|
|
||||||
span {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
||||||
<div class="page-container-grouped-styles molding-questions">
|
<questions-page-layout
|
||||||
<loadingModal ref="loadingModal" />
|
ref="questionsPageLayout"
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
:isMetaValid="meta.valid"
|
||||||
<vehicleBanner
|
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
|
||||||
:displayGenericVehicleImage="false" />
|
:questionsData="questionsData"
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
:validationRules="questions - required"
|
||||||
<div class="fade-on-route-transition sub-container make-tall">
|
v-model="selectedAnswers"
|
||||||
<alert
|
@forwardButtonAction="forwardButtonAction"
|
||||||
ref="alertFewMoreQuestions"
|
@backButtonAction="backButtonAction"
|
||||||
cmsWidgetName="AlertFewMoreQuestionsWidget"
|
:index="currentGlassIndex" />
|
||||||
class="my-5"
|
|
||||||
alertClass="alert-warning"
|
|
||||||
:manualHeadline="AlertFewMoreQuestionsHeader"
|
|
||||||
:manualCopy="AlertFewMoreQuestionsCopy"
|
|
||||||
v-bind:isDismissible="false" />
|
|
||||||
<div v-for="(glass, i) in moldingQuestionsData" :key="glass.key">
|
|
||||||
<questionChain
|
|
||||||
ref="questionChain"
|
|
||||||
v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
|
|
||||||
:questionData="glass.questions"
|
|
||||||
:glassIndex="i"
|
|
||||||
v-if="showThisQuestionChain(glass, i)"
|
|
||||||
validationRules="questions-required" />
|
|
||||||
</div>
|
|
||||||
<funnel-footer
|
|
||||||
ref="funnelFooter"
|
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
|
||||||
:isForwardActionDisabled="!meta.valid"
|
|
||||||
@back-clicked="backButtonAction"
|
|
||||||
@ForwardClicked="forwardButtonAction" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import questionsPageLayout from "@/common-components/questions-page-layout/questions-page-layout";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|
||||||
import alert from "@/ux-components/alert/alert";
|
|
||||||
import questionChain from "@/common-components/question-chain/question-chain";
|
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
|
||||||
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
|
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
|
@ -85,8 +55,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
moldingQuestionsData: store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)
|
questionsData: [],
|
||||||
.partsOrQuestions,
|
|
||||||
selectedAnswers: {},
|
selectedAnswers: {},
|
||||||
currentGlassIndex: 0,
|
currentGlassIndex: 0,
|
||||||
};
|
};
|
||||||
|
|
@ -106,15 +75,28 @@ export default {
|
||||||
// are there alreadyAnsweredQuestions?
|
// are there alreadyAnsweredQuestions?
|
||||||
const alreadyAnsweredQuestions = store.getters.damage.moldingQuestionAnswers;
|
const alreadyAnsweredQuestions = store.getters.damage.moldingQuestionAnswers;
|
||||||
|
|
||||||
this.moldingQuestionsData = this.pageData.partsOrQuestions
|
this.questionsData = this.pageData.partsOrQuestions
|
||||||
.filter((x) => x.parts[0].childPartQuestions.length)
|
.filter((x) => x.parts[0].childPartQuestions.length)
|
||||||
.map((glass, i) => {
|
.map((glass, index) => {
|
||||||
// NOTE: questions for property "questions" can differ between layouts
|
// NOTE: questions for property "questions" can differ between layouts
|
||||||
glass.questions = glass.parts[0].childPartQuestions;
|
glass.questions = glass.parts[0].childPartQuestions;
|
||||||
|
glass.answerKey = glass.glassLocation + "-" + glass.glassName;
|
||||||
// reset selectedAnswers for this glass
|
// reset selectedAnswers for this glass
|
||||||
this.selectedAnswers[glass.key] = [];
|
this.selectedAnswers[glass.answerKey] = [];
|
||||||
// pass in: glass, i, alreadyAnsweredQuestions
|
|
||||||
return this.setupInitialData(glass, i, alreadyAnsweredQuestions);
|
const updatedGlass = this.setupInitialData(glass, index, alreadyAnsweredQuestions);
|
||||||
|
|
||||||
|
this.$watch(
|
||||||
|
"selectedAnswers." + glass.answerKey,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue && Object.keys(newValue).length > 0) {
|
||||||
|
this.handleAnswerUpdates(newValue, glass.answerKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return updatedGlass;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -126,14 +108,8 @@ export default {
|
||||||
moldingQuestionsFromPageData && Object.keys(moldingQuestionsFromPageData).length > 0
|
moldingQuestionsFromPageData && Object.keys(moldingQuestionsFromPageData).length > 0
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
showThisQuestionChain(glass, i) {
|
|
||||||
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) {
|
|
||||||
return false;
|
|
||||||
} // return false if no questions or if suppressed
|
|
||||||
return this.currentGlassIndex === i || glass.answerData?.answerResult?.length > 0;
|
|
||||||
},
|
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
const questionAnswersArray = this.moldingQuestionsData.map((glass) => {
|
const questionAnswersArray = this.questionsData.map((glass) => {
|
||||||
return {
|
return {
|
||||||
glassLocation: glass.glassLocation,
|
glassLocation: glass.glassLocation,
|
||||||
glassName: glass.glassName,
|
glassName: glass.glassName,
|
||||||
|
|
@ -144,7 +120,7 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
// clear out answerData for future page loads; must occur prior to store save
|
// clear out answerData for future page loads; must occur prior to store save
|
||||||
this.moldingQuestionsData.forEach((glass) => {
|
this.questionsData.forEach((glass) => {
|
||||||
glass.answerData = {};
|
glass.answerData = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -172,313 +148,10 @@ export default {
|
||||||
|
|
||||||
this.navigateForward(partsOrQuestions);
|
this.navigateForward(partsOrQuestions);
|
||||||
},
|
},
|
||||||
handleAnswerUpdates(answer) {
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"glassIndex": 0
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// collect a list of the answered questions' numbers, needed later below
|
|
||||||
const answeredQuestionIndexes = [];
|
|
||||||
const foundDuplicateQuestions = [];
|
|
||||||
|
|
||||||
// if user has answered a question differently than anything that was preloaded,
|
|
||||||
// we need to clear out any preloaded answers
|
|
||||||
this.selectedAnswers = {};
|
|
||||||
|
|
||||||
// loop through every answered question on the currently answered glass part
|
|
||||||
answer.answeredQuestions?.forEach((aq) => {
|
|
||||||
// keep track of this question number
|
|
||||||
answeredQuestionIndexes.push(aq.questionNum);
|
|
||||||
|
|
||||||
const answeredQuestionText = aq.questionText.toUpperCase();
|
|
||||||
const answeredQuestionAnswer = aq.selectedAnswerText.toUpperCase();
|
|
||||||
|
|
||||||
// HANDLE DUPLICATE QUESTIONS
|
|
||||||
|
|
||||||
// loop through all glass parts data
|
|
||||||
this.moldingQuestionsData.forEach((glass, gpIndex) => {
|
|
||||||
// only look for duplicates forward... to parts that follow after the currently being answered part
|
|
||||||
if (gpIndex > answer.glassIndex) {
|
|
||||||
let suppressUntil;
|
|
||||||
// reset this glass part, in case user is changing their previous answers
|
|
||||||
glass.answerData = null;
|
|
||||||
glass.isSuppressedPart = null;
|
|
||||||
|
|
||||||
// loop through this glass part's part questions, looking for a questionText match
|
|
||||||
glass.questions.forEach((pq, pqIndex) => {
|
|
||||||
// clear out any previously set answers
|
|
||||||
pq.answerSelected = null;
|
|
||||||
|
|
||||||
// clear or set suppressQuestion property for each question
|
|
||||||
if (suppressUntil) {
|
|
||||||
// if suppressUntil has been set, then suppress this question if before it
|
|
||||||
if (pqIndex + 1 < suppressUntil) {
|
|
||||||
pq.suppressQuestion = true;
|
|
||||||
} else {
|
|
||||||
pq.suppressQuestion = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pq.suppressQuestion = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if these match then we have a duplicate question
|
|
||||||
if (pq.questionText.toUpperCase() === answeredQuestionText) {
|
|
||||||
const thisAnsweredPartQuestion = glass.questions[pqIndex];
|
|
||||||
let matchedAnswer;
|
|
||||||
let rejectedAnswers = []; // set as an array, in case we ever have questions with more than 2 answers...
|
|
||||||
|
|
||||||
// which one of this questions' answers matches our answer?
|
|
||||||
pq.answers.forEach((ans) => {
|
|
||||||
if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
|
|
||||||
matchedAnswer = ans;
|
|
||||||
ans.selected = true;
|
|
||||||
} else {
|
|
||||||
rejectedAnswers.push(ans);
|
|
||||||
ans.selected = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the key to re-render this part's question-chain component
|
|
||||||
this.moldingQuestionsData[gpIndex].key =
|
|
||||||
this.moldingQuestionsData[gpIndex].glassLocation +
|
|
||||||
this.moldingQuestionsData[gpIndex].glassName +
|
|
||||||
Date.now().toString();
|
|
||||||
|
|
||||||
// handle suppressing downstream in this question chain
|
|
||||||
|
|
||||||
if (matchedAnswer.nextQuestionSequence) {
|
|
||||||
// ensure that the question that the accepted answer has set to be next is NOT suppressed
|
|
||||||
glass.questions[
|
|
||||||
matchedAnswer.nextQuestionSequence - 1
|
|
||||||
].suppressQuestion = null;
|
|
||||||
// if the duplicate is the 1ST question, then set suppressUntil var to lowest nextQuestion number
|
|
||||||
if (pqIndex === 0) {
|
|
||||||
if (!suppressUntil) {
|
|
||||||
suppressUntil = matchedAnswer.nextQuestionSequence;
|
|
||||||
}
|
|
||||||
if (matchedAnswer.nextQuestionSequence < suppressUntil) {
|
|
||||||
suppressUntil = matchedAnswer.nextQuestionSequence;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle suppressing upstream in this question chain
|
|
||||||
glass.questions.forEach((q) => {
|
|
||||||
q.answers.forEach((thisAns) => {
|
|
||||||
// restore any of the answers that formerly led to the duplicated question
|
|
||||||
if (
|
|
||||||
thisAns.originalNextQuestionSequence ===
|
|
||||||
pq.questionSequence
|
|
||||||
) {
|
|
||||||
// restore original nextQuestionSequence
|
|
||||||
thisAns.nextQuestionSequence =
|
|
||||||
thisAns.originalNextQuestionSequence;
|
|
||||||
this.originalNextQuestionSequence = null;
|
|
||||||
// restore original answerResult
|
|
||||||
if (thisAns.originalAnswerResult) {
|
|
||||||
thisAns.answerResult = thisAns.originalAnswerResult;
|
|
||||||
thisAns.originalAnswerResult = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// search for any answers that lead to the duplicated question
|
|
||||||
if (thisAns.nextQuestionSequence === pq.questionSequence) {
|
|
||||||
// update either the nextQuestionSequence or the answerResult
|
|
||||||
if (matchedAnswer.nextQuestionSequence) {
|
|
||||||
thisAns.originalNextQuestionSequence =
|
|
||||||
thisAns.nextQuestionSequence;
|
|
||||||
thisAns.nextQuestionSequence =
|
|
||||||
matchedAnswer.nextQuestionSequence;
|
|
||||||
} else {
|
|
||||||
thisAns.originalNextQuestionSequence =
|
|
||||||
thisAns.nextQuestionSequence;
|
|
||||||
thisAns.nextQuestionSequence = null;
|
|
||||||
thisAns.originalAnswerResult =
|
|
||||||
thisAns.originalAnswerResult ||
|
|
||||||
thisAns.answerResult;
|
|
||||||
thisAns.answerResult = matchedAnswer.answerResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// suppress current question
|
|
||||||
thisAnsweredPartQuestion.suppressQuestion = true;
|
|
||||||
|
|
||||||
const thisGlassPart = "glass" + gpIndex;
|
|
||||||
if (foundDuplicateQuestions[thisGlassPart]) {
|
|
||||||
if (
|
|
||||||
!foundDuplicateQuestions[thisGlassPart].includes(
|
|
||||||
thisAnsweredPartQuestion.questionSequence
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
foundDuplicateQuestions[thisGlassPart].push(
|
|
||||||
thisAnsweredPartQuestion.questionSequence
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foundDuplicateQuestions[thisGlassPart] = [
|
|
||||||
thisAnsweredPartQuestion.questionSequence,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// are there any questions left that are not suppressed?
|
|
||||||
const remainingQuestions = glass.questions.filter((q) => {
|
|
||||||
return !q.suppressQuestion;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (remainingQuestions.length < 1) {
|
|
||||||
// this is the final answer for this glass part
|
|
||||||
|
|
||||||
// mark this part as completely answered by adding answerData
|
|
||||||
const answeredQuestionObj = {
|
|
||||||
questionText: pq.questionText,
|
|
||||||
selectedAnswerText: matchedAnswer.answerText,
|
|
||||||
questionNum: pq.questionSequence,
|
|
||||||
suppressQuestion: pq.suppressQuestion,
|
|
||||||
};
|
|
||||||
// set the answerData as 'already answered'
|
|
||||||
glass.answerData = {
|
|
||||||
answerResult: matchedAnswer.nextQuestionSequence
|
|
||||||
? matchedAnswer.nextQuestionSequence
|
|
||||||
: matchedAnswer.answerResult,
|
|
||||||
answeredQuestions: [answeredQuestionObj],
|
|
||||||
};
|
|
||||||
|
|
||||||
// suppress this glass because it has an answer
|
|
||||||
glass.isSuppressedPart = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the key to re-render this part's question-chain component
|
|
||||||
this.moldingQuestionsData[gpIndex].key =
|
|
||||||
this.moldingQuestionsData[gpIndex].glassLocation +
|
|
||||||
this.moldingQuestionsData[gpIndex].glassName +
|
|
||||||
Date.now().toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// DETERMINE ANSWERED QUESTIONS LIST FOR THIS GLASS PART
|
|
||||||
|
|
||||||
// look through all (this part's) part questions for any duplicates that were suppressed;
|
|
||||||
// add them to the list of answered questions if found
|
|
||||||
|
|
||||||
// EX answeredQuestionIndexes: [1,5,11,13]
|
|
||||||
|
|
||||||
// EX foundDuplicateQuestions = {
|
|
||||||
// "glassPart1": [1],
|
|
||||||
// "glassPart2": [7, 10]
|
|
||||||
// };
|
|
||||||
|
|
||||||
const thisPartsDupes = foundDuplicateQuestions["glass" + answer.glassIndex];
|
|
||||||
const completeAnsweredQuestions = answer.answeredQuestions
|
|
||||||
? [...answer.answeredQuestions]
|
|
||||||
: [];
|
|
||||||
const glassPartAnswered = this.moldingQuestionsData[answer.glassIndex];
|
|
||||||
|
|
||||||
thisPartsDupes?.forEach((dupe) => {
|
|
||||||
// dupe is a single integer
|
|
||||||
const dupeQuestion = glassPartAnswered.questions[dupe - 1];
|
|
||||||
const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);
|
|
||||||
|
|
||||||
glassPartAnswered.questions.forEach((q) => {
|
|
||||||
let includeThisDupeInAnsweredQuestions = false;
|
|
||||||
|
|
||||||
// did one of the answers of this question point to the duplicated question?
|
|
||||||
q.answers.forEach((a) => {
|
|
||||||
if (
|
|
||||||
dupe === a.originalNextQuestionSequence &&
|
|
||||||
answeredQuestionIndexes.includes(q.questionSequence) &&
|
|
||||||
a.answerText.toUpperCase() ===
|
|
||||||
dupeQuestionAnswer.answerText.toUpperCase()
|
|
||||||
) {
|
|
||||||
includeThisDupeInAnsweredQuestions = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// is this q.questionSequence listed as the duplicated question's nextQuestionSequence?
|
|
||||||
if (
|
|
||||||
q.questionSequence === dupeQuestionAnswer.nextQuestionSequence &&
|
|
||||||
answeredQuestionIndexes.includes(q.questionSequence)
|
|
||||||
) {
|
|
||||||
includeThisDupeInAnsweredQuestions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeThisDupeInAnsweredQuestions) {
|
|
||||||
completeAnsweredQuestions.push({
|
|
||||||
questionNum: dupeQuestion.questionSequence,
|
|
||||||
questionText: dupeQuestion.questionText,
|
|
||||||
selectedAnswerText: dupeQuestionAnswer.answerText,
|
|
||||||
suppressQuestion: dupeQuestion.suppressQuestion,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// make sure there are no duplicated dupes in the list...
|
|
||||||
const foundInCompleteAnsweredQuestions = new Set();
|
|
||||||
let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter((el) => {
|
|
||||||
const duplicate = foundInCompleteAnsweredQuestions.has(el.questionText);
|
|
||||||
foundInCompleteAnsweredQuestions.add(el.questionText);
|
|
||||||
return !duplicate;
|
|
||||||
});
|
|
||||||
filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort(
|
|
||||||
(a, b) => a.questionNum - b.questionNum
|
|
||||||
);
|
|
||||||
|
|
||||||
// set final answer data for the current answered glass part
|
|
||||||
glassPartAnswered.answerData = {
|
|
||||||
answerResult: answer.answerResult,
|
|
||||||
answeredQuestions: filteredCompleteAnsweredQuestions,
|
|
||||||
};
|
|
||||||
|
|
||||||
// this part has been fully answered, so advance to next molding question chain
|
|
||||||
for (let i = answer.glassIndex + 1; i < this.moldingQuestionsData.length; i++) {
|
|
||||||
// if this part has not yet been fully answered, then make it the current part
|
|
||||||
if (!this.moldingQuestionsData[i].answerData?.answerResult) {
|
|
||||||
this.currentGlassIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
|
||||||
vehicleBanner,
|
|
||||||
alert,
|
|
||||||
questionChain,
|
|
||||||
funnelSubHeader,
|
|
||||||
funnelFooter,
|
|
||||||
loadingModal,
|
|
||||||
Form,
|
Form,
|
||||||
|
questionsPageLayout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.molding-questions {
|
|
||||||
.question-text {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
span {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,27 @@
|
||||||
<template>
|
<template>
|
||||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
|
||||||
<div class="page-container-grouped-styles part-questions">
|
<questions-page-layout
|
||||||
<loadingModal ref="loadingModal" />
|
ref="questionsPageLayout"
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
:isMetaValid="meta.valid"
|
||||||
<vehicleBanner
|
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
|
||||||
cmsWidgetName="VehicleBannerWidget"
|
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
|
||||||
:displayGenericVehicleImage="false" />
|
:questionsData="questionsData"
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
:validationRules="questions - required"
|
||||||
<div class="fade-on-route-transition sub-container make-tall">
|
v-model="selectedAnswers"
|
||||||
<alert
|
@forwardButtonAction="forwardButtonAction"
|
||||||
ref="alertFewMoreQuestions"
|
@backButtonAction="backButtonAction"
|
||||||
cmsWidgetName="AlertFewMoreQuestionsWidget"
|
:index="currentGlassIndex" />
|
||||||
class="my-5"
|
|
||||||
alertClass="alert-warning"
|
|
||||||
:manualHeadline="AlertFewMoreQuestionsHeader"
|
|
||||||
:manualCopy="AlertFewMoreQuestionsCopy"
|
|
||||||
v-bind:isDismissible="false" />
|
|
||||||
<div v-for="(glass, i) in partsQuestionsData" :key="glass.key">
|
|
||||||
<questionChain
|
|
||||||
ref="questionChain"
|
|
||||||
v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
|
|
||||||
:questionData="glass.questions"
|
|
||||||
:glassIndex="i"
|
|
||||||
v-if="showThisQuestionChain(glass, i)"
|
|
||||||
validationRules="questions-required" />
|
|
||||||
</div>
|
|
||||||
<funnel-footer
|
|
||||||
ref="funnelFooter"
|
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
|
||||||
:isForwardActionDisabled="!meta.valid"
|
|
||||||
@back-clicked="backButtonAction"
|
|
||||||
@ForwardClicked="forwardButtonAction" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import questionsPageLayout from "@/common-components/questions-page-layout/questions-page-layout";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|
||||||
import alert from "@/ux-components/alert/alert";
|
|
||||||
import questionChain from "@/common-components/question-chain/question-chain";
|
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
|
||||||
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
|
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
|
@ -63,6 +33,7 @@ defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "part-questions",
|
name: "part-questions",
|
||||||
|
mixins: [vehicleQuestionsMixin],
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
// Call APIs
|
// Call APIs
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
|
|
@ -84,7 +55,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
partsQuestionsData: [],
|
questionsData: [],
|
||||||
selectedAnswers: {},
|
selectedAnswers: {},
|
||||||
currentGlassIndex: 0,
|
currentGlassIndex: 0,
|
||||||
};
|
};
|
||||||
|
|
@ -100,20 +71,33 @@ export default {
|
||||||
return this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS);
|
return this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mixins: [vehicleQuestionsMixin],
|
|
||||||
mounted() {
|
mounted() {
|
||||||
// are there alreadyAnsweredQuestions?
|
// are there alreadyAnsweredQuestions?
|
||||||
const alreadyAnsweredQuestions = store.getters.damage.partQuestionAnswers;
|
const alreadyAnsweredQuestions = store.getters.damage.partQuestionAnswers;
|
||||||
|
|
||||||
this.partsQuestionsData = this.pageData.partsOrQuestions
|
this.questionsData = this.pageData.partsOrQuestions
|
||||||
.filter((x) => x.partQuestions)
|
.filter((x) => x.partQuestions)
|
||||||
.map((glass, i) => {
|
.map((glass, index) => {
|
||||||
// NOTE: questions for property "questions" can differ between layouts
|
// NOTE: questions for property "questions" can differ between layouts
|
||||||
glass.questions = glass.partQuestions;
|
glass.questions = glass.partQuestions;
|
||||||
|
glass.answerKey = glass.glassLocation + "-" + glass.glassName;
|
||||||
// reset selectedAnswers for this glass
|
// reset selectedAnswers for this glass
|
||||||
this.selectedAnswers[glass.key] = [];
|
this.selectedAnswers[glass.answerKey] = [];
|
||||||
// pass in: glass, i, alreadyAnsweredQuestions
|
|
||||||
return this.setupInitialData(glass, i, alreadyAnsweredQuestions);
|
const updatedGlass = this.setupInitialData(glass, index, alreadyAnsweredQuestions);
|
||||||
|
|
||||||
|
// Set up watch for each set of glass questions
|
||||||
|
this.$watch(
|
||||||
|
"selectedAnswers." + glass.answerKey,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue && Object.keys(newValue).length > 0) {
|
||||||
|
this.handleAnswerUpdates(newValue, glass.answerKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return updatedGlass;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -121,14 +105,8 @@ export default {
|
||||||
const partQuestionsFromPageData = store.getters.pageData(fmgPageValues.PART_QUESTIONS);
|
const partQuestionsFromPageData = store.getters.pageData(fmgPageValues.PART_QUESTIONS);
|
||||||
return partQuestionsFromPageData && Object.keys(partQuestionsFromPageData).length > 0;
|
return partQuestionsFromPageData && Object.keys(partQuestionsFromPageData).length > 0;
|
||||||
},
|
},
|
||||||
showThisQuestionChain(glass, i) {
|
|
||||||
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) {
|
|
||||||
return false;
|
|
||||||
} // return false if no questions or if suppressed
|
|
||||||
return this.currentGlassIndex === i || glass.answerData?.answerResult?.length > 0;
|
|
||||||
},
|
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
const questionAnswersArray = this.partsQuestionsData.map((glass) => {
|
const questionAnswersArray = this.questionsData.map((glass) => {
|
||||||
return {
|
return {
|
||||||
glassLocation: glass.glassLocation,
|
glassLocation: glass.glassLocation,
|
||||||
glassName: glass.glassName,
|
glassName: glass.glassName,
|
||||||
|
|
@ -139,7 +117,7 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
// clear out answerData for future page loads; must occur prior to store save
|
// clear out answerData for future page loads; must occur prior to store save
|
||||||
this.partsQuestionsData.forEach((glass) => {
|
this.questionsData.forEach((glass) => {
|
||||||
glass.answerData = {};
|
glass.answerData = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -151,321 +129,22 @@ export default {
|
||||||
);
|
);
|
||||||
|
|
||||||
// call API parts method
|
// call API parts method
|
||||||
const partsLookup = await this.dispatchStoreAction(storeActions.GET_PARTS).catch(() => {
|
const partsLookup = await this.dispatchStoreAction(this.storeActions.GET_PARTS).catch(
|
||||||
return this.$refs.funnelFooter.removeLoader();
|
() => {
|
||||||
});
|
return this.$refs.funnelFooter.removeLoader();
|
||||||
|
|
||||||
const glassPiecePartsForStore = partsLookup.data.glassPieceParts;
|
|
||||||
|
|
||||||
this.navigateForward(glassPiecePartsForStore);
|
|
||||||
},
|
|
||||||
handleAnswerUpdates(answer) {
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"glassIndex": 0
|
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// collect a list of the answered questions' numbers, needed later below
|
|
||||||
const answeredQuestionIndexes = [];
|
|
||||||
const foundDuplicateQuestions = [];
|
|
||||||
|
|
||||||
// if user has answered a question differently than anything that was preloaded,
|
|
||||||
// we need to clear out any preloaded answers
|
|
||||||
this.selectedAnswers = {};
|
|
||||||
|
|
||||||
// loop through every answered question on the currently answered glass part
|
|
||||||
answer.answeredQuestions?.forEach((aq) => {
|
|
||||||
// keep track of this question number
|
|
||||||
answeredQuestionIndexes.push(aq.questionNum);
|
|
||||||
|
|
||||||
const answeredQuestionText = aq.questionText.toUpperCase();
|
|
||||||
const answeredQuestionAnswer = aq.selectedAnswerText.toUpperCase();
|
|
||||||
|
|
||||||
// HANDLE DUPLICATE QUESTIONS
|
|
||||||
|
|
||||||
// loop through all glass parts data
|
|
||||||
this.partsQuestionsData.forEach((glass, gpIndex) => {
|
|
||||||
// only look for duplicates forward... to parts that follow after the currently being answered part
|
|
||||||
if (gpIndex > answer.glassIndex) {
|
|
||||||
let suppressUntil;
|
|
||||||
// reset this glass part, in case user is changing their previous answers
|
|
||||||
glass.answerData = null;
|
|
||||||
glass.isSuppressedPart = null;
|
|
||||||
|
|
||||||
// loop through this glass part's questions, looking for a questionText match
|
|
||||||
glass.questions.forEach((pq, pqIndex) => {
|
|
||||||
// clear out any previously set answers
|
|
||||||
pq.answerSelected = null;
|
|
||||||
|
|
||||||
// clear or set suppressQuestion property for each question
|
|
||||||
if (suppressUntil) {
|
|
||||||
// if suppressUntil has been set, then suppress this question if before it
|
|
||||||
if (pqIndex + 1 < suppressUntil) {
|
|
||||||
pq.suppressQuestion = true;
|
|
||||||
} else {
|
|
||||||
pq.suppressQuestion = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pq.suppressQuestion = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if these match then we have a duplicate question
|
|
||||||
if (pq.questionText.toUpperCase() === answeredQuestionText) {
|
|
||||||
const thisAnsweredQuestion = glass.questions[pqIndex];
|
|
||||||
let matchedAnswer;
|
|
||||||
let rejectedAnswers = []; // set as an array, in case we ever have questions with more than 2 answers...
|
|
||||||
|
|
||||||
// which one of this questions' answers matches our answer?
|
|
||||||
pq.answers.forEach((ans) => {
|
|
||||||
if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
|
|
||||||
matchedAnswer = ans;
|
|
||||||
ans.selected = true;
|
|
||||||
} else {
|
|
||||||
rejectedAnswers.push(ans);
|
|
||||||
ans.selected = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the key to re-render this part's question-chain component
|
|
||||||
this.partsQuestionsData[gpIndex].key =
|
|
||||||
this.partsQuestionsData[gpIndex].glassLocation +
|
|
||||||
this.partsQuestionsData[gpIndex].glassName +
|
|
||||||
Date.now().toString();
|
|
||||||
|
|
||||||
// handle suppressing downstream in this question chain
|
|
||||||
|
|
||||||
if (matchedAnswer.nextQuestionSequence) {
|
|
||||||
// ensure that the question that the accepted answer has set to be next is NOT suppressed
|
|
||||||
glass.questions[
|
|
||||||
matchedAnswer.nextQuestionSequence - 1
|
|
||||||
].suppressQuestion = null;
|
|
||||||
// if the duplicate is the 1ST question, then set suppressUntil var to lowest nextQuestion number
|
|
||||||
if (pqIndex === 0) {
|
|
||||||
if (!suppressUntil) {
|
|
||||||
suppressUntil = matchedAnswer.nextQuestionSequence;
|
|
||||||
}
|
|
||||||
if (matchedAnswer.nextQuestionSequence < suppressUntil) {
|
|
||||||
suppressUntil = matchedAnswer.nextQuestionSequence;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle suppressing upstream in this question chain
|
|
||||||
glass.questions.forEach((q) => {
|
|
||||||
q.answers.forEach((thisAns) => {
|
|
||||||
// restore any of the answers that formerly led to the duplicated question
|
|
||||||
if (
|
|
||||||
thisAns.originalNextQuestionSequence ===
|
|
||||||
pq.questionSequence
|
|
||||||
) {
|
|
||||||
// restore original nextQuestionSequence
|
|
||||||
thisAns.nextQuestionSequence =
|
|
||||||
thisAns.originalNextQuestionSequence;
|
|
||||||
this.originalNextQuestionSequence = null;
|
|
||||||
// restore original answerResult
|
|
||||||
if (thisAns.originalAnswerResult) {
|
|
||||||
thisAns.answerResult = thisAns.originalAnswerResult;
|
|
||||||
thisAns.originalAnswerResult = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// search for any answers that lead to the duplicated question
|
|
||||||
if (thisAns.nextQuestionSequence === pq.questionSequence) {
|
|
||||||
// update either the nextQuestionSequence or the answerResult
|
|
||||||
if (matchedAnswer.nextQuestionSequence) {
|
|
||||||
thisAns.originalNextQuestionSequence =
|
|
||||||
thisAns.nextQuestionSequence;
|
|
||||||
thisAns.nextQuestionSequence =
|
|
||||||
matchedAnswer.nextQuestionSequence;
|
|
||||||
} else {
|
|
||||||
thisAns.originalNextQuestionSequence =
|
|
||||||
thisAns.nextQuestionSequence;
|
|
||||||
thisAns.nextQuestionSequence = null;
|
|
||||||
thisAns.originalAnswerResult =
|
|
||||||
thisAns.originalAnswerResult ||
|
|
||||||
thisAns.answerResult;
|
|
||||||
thisAns.answerResult = matchedAnswer.answerResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// suppress current question
|
|
||||||
thisAnsweredQuestion.suppressQuestion = true;
|
|
||||||
|
|
||||||
const thisGlassPart = "glass" + gpIndex;
|
|
||||||
if (foundDuplicateQuestions[thisGlassPart]) {
|
|
||||||
if (
|
|
||||||
!foundDuplicateQuestions[thisGlassPart].includes(
|
|
||||||
thisAnsweredQuestion.questionSequence
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
foundDuplicateQuestions[thisGlassPart].push(
|
|
||||||
thisAnsweredQuestion.questionSequence
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foundDuplicateQuestions[thisGlassPart] = [
|
|
||||||
thisAnsweredQuestion.questionSequence,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// are there any questions left that are not suppressed?
|
|
||||||
const remainingQuestions = glass.questions.filter((q) => {
|
|
||||||
return !q.suppressQuestion;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (remainingQuestions.length < 1) {
|
|
||||||
// this is the final answer for this glass part
|
|
||||||
|
|
||||||
// mark this part as completely answered by adding answerData
|
|
||||||
const answeredQuestionObj = {
|
|
||||||
questionText: pq.questionText,
|
|
||||||
selectedAnswerText: matchedAnswer.answerText,
|
|
||||||
questionNum: pq.questionSequence,
|
|
||||||
suppressQuestion: pq.suppressQuestion,
|
|
||||||
};
|
|
||||||
// set the answerData as 'already answered'
|
|
||||||
glass.answerData = {
|
|
||||||
answerResult: matchedAnswer.nextQuestionSequence
|
|
||||||
? matchedAnswer.nextQuestionSequence
|
|
||||||
: matchedAnswer.answerResult,
|
|
||||||
answeredQuestions: [answeredQuestionObj],
|
|
||||||
};
|
|
||||||
|
|
||||||
// suppress this glass because it has an answer
|
|
||||||
glass.isSuppressedPart = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the key to re-render this part's question-chain component
|
|
||||||
this.partsQuestionsData[gpIndex].key =
|
|
||||||
this.partsQuestionsData[gpIndex].glassLocation +
|
|
||||||
this.partsQuestionsData[gpIndex].glassName +
|
|
||||||
Date.now().toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// DETERMINE ANSWERED QUESTIONS LIST FOR THIS GLASS PART
|
|
||||||
|
|
||||||
// look through all (this part's) part questions for any duplicates that were suppressed;
|
|
||||||
// add them to the list of answered questions if found
|
|
||||||
|
|
||||||
// EX answeredQuestionIndexes: [1,5,11,13]
|
|
||||||
|
|
||||||
// EX foundDuplicateQuestions = {
|
|
||||||
// "glassPart1": [1],
|
|
||||||
// "glassPart2": [7, 10]
|
|
||||||
// };
|
|
||||||
|
|
||||||
const thisPartsDupes = foundDuplicateQuestions["glass" + answer.glassIndex];
|
|
||||||
const completeAnsweredQuestions = answer.answeredQuestions
|
|
||||||
? [...answer.answeredQuestions]
|
|
||||||
: [];
|
|
||||||
const glassPartAnswered = this.partsQuestionsData[answer.glassIndex];
|
|
||||||
|
|
||||||
thisPartsDupes?.forEach((dupe) => {
|
|
||||||
// dupe is a single integer
|
|
||||||
const dupeQuestion = glassPartAnswered.questions[dupe - 1];
|
|
||||||
const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);
|
|
||||||
|
|
||||||
glassPartAnswered.questions.forEach((q) => {
|
|
||||||
let includeThisDupeInAnsweredQuestions = false;
|
|
||||||
|
|
||||||
// did one of the answers of this question point to the duplicated question?
|
|
||||||
q.answers.forEach((a) => {
|
|
||||||
if (
|
|
||||||
dupe === a.originalNextQuestionSequence &&
|
|
||||||
answeredQuestionIndexes.includes(q.questionSequence) &&
|
|
||||||
a.answerText.toUpperCase() ===
|
|
||||||
dupeQuestionAnswer.answerText.toUpperCase()
|
|
||||||
) {
|
|
||||||
includeThisDupeInAnsweredQuestions = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// is this q.questionSequence listed as the duplicated question's nextQuestionSequence?
|
|
||||||
if (
|
|
||||||
q.questionSequence === dupeQuestionAnswer.nextQuestionSequence &&
|
|
||||||
answeredQuestionIndexes.includes(q.questionSequence)
|
|
||||||
) {
|
|
||||||
includeThisDupeInAnsweredQuestions = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (includeThisDupeInAnsweredQuestions) {
|
|
||||||
completeAnsweredQuestions.push({
|
|
||||||
questionNum: dupeQuestion.questionSequence,
|
|
||||||
questionText: dupeQuestion.questionText,
|
|
||||||
selectedAnswerText: dupeQuestionAnswer.answerText,
|
|
||||||
suppressQuestion: dupeQuestion.suppressQuestion,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// make sure there are no duplicated dupes in the list...
|
|
||||||
const foundInCompleteAnsweredQuestions = new Set();
|
|
||||||
let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter((el) => {
|
|
||||||
const duplicate = foundInCompleteAnsweredQuestions.has(el.questionText);
|
|
||||||
foundInCompleteAnsweredQuestions.add(el.questionText);
|
|
||||||
return !duplicate;
|
|
||||||
});
|
|
||||||
filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort(
|
|
||||||
(a, b) => a.questionNum - b.questionNum
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// set final answer data for the current answered glass part
|
const glassPartsForStore = partsLookup.data.glassPieceParts;
|
||||||
glassPartAnswered.answerData = {
|
|
||||||
answerResult: answer.answerResult,
|
|
||||||
answeredQuestions: filteredCompleteAnsweredQuestions,
|
|
||||||
};
|
|
||||||
|
|
||||||
// this part has been fully answered, so advance to next part's question chain
|
debugger; // eslint-disable-line no-debugger
|
||||||
for (let i = answer.glassIndex + 1; i < this.partsQuestionsData.length; i++) {
|
|
||||||
// if this part has not yet been fully answered, then make it the current part
|
this.navigateForward(glassPartsForStore);
|
||||||
if (!this.partsQuestionsData[i].answerData?.answerResult) {
|
|
||||||
this.currentGlassIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
|
||||||
vehicleBanner,
|
|
||||||
alert,
|
|
||||||
questionChain,
|
|
||||||
funnelSubHeader,
|
|
||||||
funnelFooter,
|
|
||||||
loadingModal,
|
|
||||||
Form,
|
Form,
|
||||||
|
questionsPageLayout,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.part-questions {
|
|
||||||
.question-text {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
span {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { storeMutations } from "@/constants/store-mutations.js";
|
import { storeMutations } from "@/constants/store-mutations.js";
|
||||||
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
|
||||||
import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
@ -67,9 +66,9 @@ export default {
|
||||||
currentPageComesAfterPage(currentPage = this.$route.query.fmgPage, fmgPage) {
|
currentPageComesAfterPage(currentPage = this.$route.query.fmgPage, fmgPage) {
|
||||||
return this.comparePageIndices(currentPage, fmgPage) > 0;
|
return this.comparePageIndices(currentPage, fmgPage) > 0;
|
||||||
},
|
},
|
||||||
setupInitialData(glass, i, alreadyAnsweredQuestions, vm) {
|
setupInitialData(glass, index, alreadyAnsweredQuestions, vm) {
|
||||||
glass.key = glass.glassLocation + "-" + glass.glassName;
|
|
||||||
const self = vm ?? this;
|
const self = vm ?? this;
|
||||||
|
|
||||||
// clear answerData if no questions are already answered
|
// clear answerData if no questions are already answered
|
||||||
if (!alreadyAnsweredQuestions) {
|
if (!alreadyAnsweredQuestions) {
|
||||||
glass.answerData = null;
|
glass.answerData = null;
|
||||||
|
|
@ -94,33 +93,39 @@ export default {
|
||||||
let answerString = "";
|
let answerString = "";
|
||||||
|
|
||||||
// loop through answeredQuestions for matches
|
// loop through answeredQuestions for matches
|
||||||
answeredGlass.answeredQuestions.forEach((aq) => {
|
answeredGlass.answeredQuestions.forEach((answeredQuestion) => {
|
||||||
if (!aq.questionNum || !aq.selectedAnswerText) {
|
if (!answeredQuestion.questionNum || !answeredQuestion.selectedAnswerText) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// determine which answer was previously chosen
|
// determine which answer was previously chosen
|
||||||
const chosenAns = glass.questions[aq.questionNum - 1].answers.find((a) => {
|
const chosenAns = glass.questions[
|
||||||
|
answeredQuestion.questionNum - 1
|
||||||
|
].answers.find((a) => {
|
||||||
return (
|
return (
|
||||||
a.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase()
|
a.answerText.toUpperCase() ===
|
||||||
|
answeredQuestion.selectedAnswerText.toUpperCase()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// set the answerString to use for answerSelected
|
// set the answerString to use for answerSelected
|
||||||
if (chosenAns.nextQuestionSequence) {
|
if (chosenAns.nextQuestionSequence) {
|
||||||
answerString = `${aq.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`;
|
answerString = `${answeredQuestion.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`;
|
||||||
} else {
|
} else {
|
||||||
answerString = `${aq.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}`;
|
answerString = `${answeredQuestion.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark this question as answered (question-chain will read this)
|
// mark this question as answered (question-chain will read this)
|
||||||
glass.questions[aq.questionNum - 1].answerSelected = answerString;
|
glass.questions[answeredQuestion.questionNum - 1].answerSelected =
|
||||||
|
answerString;
|
||||||
// mark this question as suppressed if needed (question-chain uses this)
|
// mark this question as suppressed if needed (question-chain uses this)
|
||||||
if (aq.suppressQuestion) {
|
if (answeredQuestion.suppressThisQuestion) {
|
||||||
glass.questions[aq.questionNum - 1].suppressQuestion = true;
|
glass.questions[
|
||||||
|
answeredQuestion.questionNum - 1
|
||||||
|
].suppressThisQuestion = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// advance the currentGlassIndex
|
// advance the currentGlassIndex
|
||||||
self.currentGlassIndex = i;
|
self.currentGlassIndex = index;
|
||||||
|
|
||||||
const answerResult = answeredGlass.partNum
|
const answerResult = answeredGlass.partNum
|
||||||
? answeredGlass.partNum
|
? answeredGlass.partNum
|
||||||
|
|
@ -135,20 +140,221 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up watch for each set of glass questions
|
|
||||||
// (updated when all questions for a glass have been answered in question-chain)
|
|
||||||
self.$watch(
|
|
||||||
"selectedAnswers." + glass.glassLocation + "-" + glass.glassName,
|
|
||||||
(newValue) => {
|
|
||||||
if (newValue) {
|
|
||||||
self.handleAnswerUpdates(newValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
return glass;
|
return glass;
|
||||||
},
|
},
|
||||||
|
handleAnswerUpdates(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) => {
|
||||||
|
/* answeredQuestion example format:
|
||||||
|
{
|
||||||
|
"questionText": "Is your Grand Cherokee the Laredo model?",
|
||||||
|
"selectedAnswerText": "Yes",
|
||||||
|
"questionNum": 1,
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
const answeredQuestionText = answeredQuestion.questionText.toUpperCase();
|
||||||
|
const answeredQuestionAnswer = answeredQuestion.selectedAnswerText.toUpperCase();
|
||||||
|
|
||||||
|
// HANDLE DUPLICATE QUESTIONS
|
||||||
|
|
||||||
|
// loop through all glass data
|
||||||
|
self.questionsData.forEach((glass, glassIndex) => {
|
||||||
|
/* glass example format:
|
||||||
|
{
|
||||||
|
"glassName": "Single",
|
||||||
|
"glassLocation": "Windshield",
|
||||||
|
"parts": null,
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"questionSequence": 1,
|
||||||
|
"questionText": "Is your vehicle equipped with a black dotted pattern behind the rear view mirror, known as a third visor frit?",
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"answerResult": "DW01537",
|
||||||
|
"answerText": "Yes",
|
||||||
|
"nextQuestionSequence": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answerResult": "DW01537b",
|
||||||
|
"answerText": "No",
|
||||||
|
"nextQuestionSequence": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"answerKey": "Windshield-Single",
|
||||||
|
"answerData": null
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// limit duplicate search to glass pieces that 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
|
||||||
|
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() === answeredQuestionAnswer) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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
|
// Can't use `this` because navigateForward is also called from vin-pages-mixin
|
||||||
async navigateForward(partsOrQuestions, vm) {
|
async navigateForward(partsOrQuestions, vm) {
|
||||||
const self = vm ?? this;
|
const self = vm ?? this;
|
||||||
|
|
@ -240,10 +446,16 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
// if single parts only
|
// if single parts only
|
||||||
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
|
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
|
||||||
|
|
||||||
// save to store lineItems.glassParts
|
// save to store lineItems.glassParts
|
||||||
self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
|
self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
|
||||||
|
|
||||||
self.$refs.loadingModal.showModal();
|
if (self.$refs.questionsPage) {
|
||||||
|
self.$refs.questionsPage.showLoadingModal();
|
||||||
|
} else if (self.$refs.loadingModal) {
|
||||||
|
self.$refs.loadingModal.showModal();
|
||||||
|
}
|
||||||
|
|
||||||
navigateToHeritageFunnel();
|
navigateToHeritageFunnel();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -337,24 +337,6 @@ describe("vehicle-questions-mixin", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("setupInitialData", () => {
|
describe("setupInitialData", () => {
|
||||||
describe("should always", () => {
|
|
||||||
test("return glass with new key attribute", async () => {
|
|
||||||
// Arrange
|
|
||||||
const { wrapper } = setupMocks({});
|
|
||||||
const glass = {
|
|
||||||
glassName: "Single",
|
|
||||||
glassLocation: "Windshield",
|
|
||||||
};
|
|
||||||
const i = 0;
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const returnedGlass = await wrapper.vm.setupInitialData(glass, i);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(returnedGlass).toMatchObject({ key: "Windshield-Single" });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("if no alreadyAnsweredQuestions", () => {
|
describe("if no alreadyAnsweredQuestions", () => {
|
||||||
test("should return glass with answerData of null", async () => {
|
test("should return glass with answerData of null", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -430,6 +412,816 @@ describe("vehicle-questions-mixin", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("handleAnswerUpdates", () => {
|
||||||
|
describe("selectedAnswers", () => {
|
||||||
|
test("should be cleared to be empty", () => {
|
||||||
|
// Arrange
|
||||||
|
const answer = {
|
||||||
|
answerResult: "DD11132",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "Yes",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.selectedAnswers = { test: "mockSelectedAnswers" };
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [],
|
||||||
|
answerData: "testAnswerData1",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.selectedAnswers).toMatchObject({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("questions in glass parts that are after the answered glass", () => {
|
||||||
|
test("should have answerData cleared", () => {
|
||||||
|
// Arrange
|
||||||
|
const answer = {
|
||||||
|
answerResult: "DD11132",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "Yes",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [],
|
||||||
|
answerData: "testAnswerData1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [],
|
||||||
|
answerData: "testAnswerData2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.questionsData[1].answerData).toEqual(null);
|
||||||
|
});
|
||||||
|
test("should have isSuppressedPart cleared", () => {
|
||||||
|
// Arrange
|
||||||
|
const answer = {
|
||||||
|
answerResult: "DD11132",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "Yes",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [],
|
||||||
|
answerData: "testAnswerData1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [],
|
||||||
|
answerData: "testAnswerData2",
|
||||||
|
isSuppressedPart: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.questionsData[1].isSuppressedPart).toEqual(null);
|
||||||
|
});
|
||||||
|
test("should set answerSelected for each glass part question to null ", () => {
|
||||||
|
// Arrange
|
||||||
|
const answer = {
|
||||||
|
answerResult: "DD11132",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "Yes",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [],
|
||||||
|
answerData: "testAnswerData1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText:
|
||||||
|
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
answerSelected: "456",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 2,
|
||||||
|
questionText:
|
||||||
|
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "FW04848",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "FW04846",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
answerSelected: "789",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
answerData: "testAnswerData2",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.questionsData[1].questions[0].answerSelected).toEqual(null);
|
||||||
|
expect(wrapper.vm.questionsData[1].questions[1].answerSelected).toEqual(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("if there is a duplicate question", () => {
|
||||||
|
test("then the glass piece's key should be updated", () => {
|
||||||
|
// Arrange
|
||||||
|
const answerNo = {
|
||||||
|
answerResult: "456",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "No",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
key: "testkey1",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
key: "testkey2",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "234",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
|
||||||
|
const glassWithDuplicate = wrapper.vm.questionsData[1];
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(glassWithDuplicate.key).not.toBe("testkey2");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("then that question should be supressed", () => {
|
||||||
|
// Arrange
|
||||||
|
const answerNo = {
|
||||||
|
answerResult: "456",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "No",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "234",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
|
||||||
|
const duplicateQuestion = wrapper.vm.questionsData[1].questions[0];
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(duplicateQuestion.suppressThisQuestion).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the duplicate has a nextQuestionSequence value", () => {
|
||||||
|
test("then the question set as nextQuestionSequence should not be suppressed", () => {
|
||||||
|
// Arrange
|
||||||
|
const answerNo = {
|
||||||
|
answerResult: "456",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "No",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "ZZZTest duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 2,
|
||||||
|
questionText: "Test question 2?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "234",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 3,
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "345",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
suppressThisQuestion: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
|
||||||
|
const nextQuestionAfterDuplicate = wrapper.vm.questionsData[1].questions[2];
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(nextQuestionAfterDuplicate.suppressThisQuestion).not.toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("then the nextQuestionSequence should be updated and the original value saved", () => {
|
||||||
|
// Arrange
|
||||||
|
const answerNo = {
|
||||||
|
answerResult: "456",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
selectedAnswerText: "No",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 2,
|
||||||
|
questionText: "Test question 2?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "234",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 3,
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: 5,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 4,
|
||||||
|
questionText: "Test question 4?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "567",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "678",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 5,
|
||||||
|
questionText: "Test question 5?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "789",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "890",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
|
||||||
|
const duplicatedQuestion = wrapper.vm.questionsData[1].questions[2];
|
||||||
|
const duplicatedQuestionAnswer = duplicatedQuestion.answers.filter((a) => {
|
||||||
|
return a.selected;
|
||||||
|
});
|
||||||
|
const answerToTest = wrapper.vm.questionsData[1].questions[0].answers.filter(
|
||||||
|
(a) => {
|
||||||
|
return a.originalNextQuestionSequence;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(answerToTest[0].originalNextQuestionSequence).toEqual(
|
||||||
|
duplicatedQuestion.questionSequence
|
||||||
|
);
|
||||||
|
expect(answerToTest[0].nextQuestionSequence).toEqual(
|
||||||
|
duplicatedQuestionAnswer[0].nextQuestionSequence
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the duplicate was the first question for that part", () => {
|
||||||
|
test("then any questions up to the nextQuestionSequence value should be marked as suppressed", () => {
|
||||||
|
// Arrange
|
||||||
|
const answerNo = {
|
||||||
|
answerResult: "456",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
selectedAnswerText: "No",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test duplicate question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 2,
|
||||||
|
questionText: "Test question 2?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "234",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 3,
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "345",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(
|
||||||
|
wrapper.vm.questionsData[1].questions[0].suppressThisQuestion
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
wrapper.vm.questionsData[1].questions[1].suppressThisQuestion
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
wrapper.vm.questionsData[1].questions[2].suppressThisQuestion
|
||||||
|
).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the duplicate has an answerResult", () => {
|
||||||
|
test("then any questions in the same glass piece that lead to the duplicate should be modified to just provide the duplicate's answerResult", async () => {
|
||||||
|
// Arrange
|
||||||
|
const answerNo = {
|
||||||
|
answerResult: "456",
|
||||||
|
answeredQuestions: [
|
||||||
|
{
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
selectedAnswerText: "No",
|
||||||
|
questionNum: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
wrapper.vm.questionsData = [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front",
|
||||||
|
questions: [
|
||||||
|
{
|
||||||
|
questionSequence: 1,
|
||||||
|
questionText: "Test question 1?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: 3,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 2,
|
||||||
|
questionText: "Test question 2?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "123",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "234",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionSequence: 3,
|
||||||
|
questionText: "Test question 3?",
|
||||||
|
answers: [
|
||||||
|
{
|
||||||
|
answerResult: "345",
|
||||||
|
answerText: "Yes",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
answerResult: "456",
|
||||||
|
answerText: "No",
|
||||||
|
nextQuestionSequence: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
|
||||||
|
|
||||||
|
const questionsToTest = wrapper.vm.questionsData[1].questions;
|
||||||
|
const answerLeadingToDuplicate = questionsToTest[0].answers[1];
|
||||||
|
const duplicateQuestion = questionsToTest[2];
|
||||||
|
const answerInDuplicateQuestion = duplicateQuestion.answers.filter((a) => {
|
||||||
|
return a.selected;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(answerLeadingToDuplicate.answerResult).toEqual(
|
||||||
|
answerInDuplicateQuestion[0].answerResult
|
||||||
|
);
|
||||||
|
expect(answerLeadingToDuplicate.originalAnswerResult).toBeFalsy();
|
||||||
|
expect(answerLeadingToDuplicate.nextQuestionSequence).toEqual(null);
|
||||||
|
expect(answerLeadingToDuplicate.originalNextQuestionSequence).toEqual(
|
||||||
|
duplicateQuestion.questionSequence
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("navigateForward", () => {
|
describe("navigateForward", () => {
|
||||||
describe("should go to parts-questions", () => {
|
describe("should go to parts-questions", () => {
|
||||||
test("single glass location has part question => go to parts-questions", async () => {
|
test("single glass location has part question => go to parts-questions", async () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue