This commit is contained in:
FrankRua 2022-07-01 13:23:27 -04:00
parent 1afdec7138
commit ad2a4a503e
2 changed files with 225 additions and 226 deletions

View file

@ -141,7 +141,7 @@ describe("vehicle-parts.vue", () => {
//Arrange //Arrange
store.getters.pageData.mockReturnValueOnce(basePartResponse); store.getters.pageData.mockReturnValueOnce(basePartResponse);
store.getters.lineItems = { glassParts: [{ partNumber: 'DB12209YPYNOEM'} ] } store.getters.lineItems = { glassParts: [{ partNumber: 'DB12209YPYNOEM' }] }
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
mountOptionsMockData: { mountOptionsMockData: {
@ -217,7 +217,7 @@ describe("vehicle-parts.vue", () => {
store.getters.pageData.mockReturnValueOnce(basePartResponse); store.getters.pageData.mockReturnValueOnce(basePartResponse);
store.getters.lineItems = { glassParts: {} } store.getters.lineItems = { glassParts: {} }
store.commit = jest.fn(); store.commit = jest.fn();
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
mountOptionsMockData: { mountOptionsMockData: {
router: { router: {
@ -236,7 +236,7 @@ describe("vehicle-parts.vue", () => {
} }
}); });
wrapper.setData({ glassParts: {"Rear-Stationary": { partNumber: 'DB12209GTYN'}}}); wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } });
//Act //Act
vehicleParts.beforeRouteEnter.call( vehicleParts.beforeRouteEnter.call(

View file

@ -1,51 +1,51 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
<div class="page-container-grouped-styles vehicle-parts"> <div class="page-container-grouped-styles vehicle-parts">
<funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner <vehicleBanner
ref="vehicleBanner" ref="vehicleBanner"
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" :displayGenericVehicleImage="false"
/> />
<funnelSubHeader <funnelSubHeader
ref="funnelSubHeader" ref="funnelSubHeader"
cmsWidgetName="FunnelSubHeaderWidget" cmsWidgetName="FunnelSubHeaderWidget"
/> />
<div class="fade-on-route-transition sub-container make-tall"> <div class="fade-on-route-transition sub-container make-tall">
<div class="prevent-squish my-5"> <div class="prevent-squish my-5">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<alert <alert
class="rounded border-0 shadow-sm" class="rounded border-0 shadow-sm"
alertClass="alert-warning" alertClass="alert-warning"
cmsWidgetName="AlertWidget" cmsWidgetName="AlertWidget"
:isDismissible="false" :isDismissible="false"
/> />
</div> </div>
</div> </div>
</div> </div>
<div v-for="(item, i) in PartsOrQuestions" :key="i"> <div v-for="(item, i) in PartsOrQuestions" :key="i">
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) --> <!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
<hr v-if="i > 0" /> <hr v-if="i > 0" />
<glassPartQuestion <glassPartQuestion
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`" :ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
v-model="glassParts[item.glassLocation + '-' + item.glassName]" v-model="glassParts[item.glassLocation + '-' + item.glassName]"
:glassLocation="item.glassLocation" :glassLocation="item.glassLocation"
:glassName="item.glassName" :glassName="item.glassName"
:colorAnswers="item.colorAnswers" :colorAnswers="item.colorAnswers"
:alreadyPopulatedPartsData="alreadyPopulatedPartsData" :alreadyPopulatedPartsData="alreadyPopulatedPartsData"
/> />
</div> </div>
<funnelFooter <funnelFooter
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="funnelFooter"
:isForwardActionDisabled="isForwardActionDisabled" :isForwardActionDisabled="isForwardActionDisabled"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
/> />
</div> </div>
</div> </div>
</Form> </Form>
</template> </template>
<script> <script>
@ -60,202 +60,201 @@ import alert from "@/ux-components/alert/alert";
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 { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { storeMutations } from "@/constants/store-mutations";
import store from "@/store"; import store from "@/store";
import { Form, defineRule } from "vee-validate"; import { Form } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
export default { export default {
name: "vehicle-parts", name: "vehicle-parts",
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);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
resultKey: "cmsContent", resultKey: "cmsContent",
promise: cmsContentPromise, promise: cmsContentPromise,
}, },
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page. // Call the "next" function to complete the transition to this page.
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
// Glass Part Question dynamic component // Glass Part Question dynamic component
Object.keys(vm.$refs) Object.keys(vm.$refs)
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined) .filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
.forEach((c) => .forEach((c) =>
vm.$refs[c][0].initializeComponent({ vm.$refs[c][0].initializeComponent({
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget, ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget, FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
}) })
); );
}); });
}, },
data() { data() {
return { return {
glassParts: {}, glassParts: {},
alertWidgetData: Object, alertWidgetData: Object,
alreadyPopulatedPartsData: {}, alreadyPopulatedPartsData: {},
}; };
}, },
components: { components: {
Form, Form,
glassPartQuestion, glassPartQuestion,
funnelHeader, funnelHeader,
vehicleBanner, vehicleBanner,
funnelSubHeader, funnelSubHeader,
funnelFooter, funnelFooter,
alert, alert,
}, },
computed: { computed: {
isForwardActionDisabled() { isForwardActionDisabled() {
return ( return (
Object.keys(this.matchedParts).length !== Object.keys(this.matchedParts).length !==
this.PartsFromApi.partsOrQuestions.length this.PartsFromApi.partsOrQuestions.length
); );
}, },
matchedParts() { matchedParts() {
const matchedParts = []; const matchedParts = [];
// Match them to the parts from the API. // Match them to the parts from the API.
this.PartsFromApi.partsOrQuestions.forEach((part) => { this.PartsFromApi.partsOrQuestions.forEach((part) => {
const selectedPartForGlassLocationAndName = const selectedPartForGlassLocationAndName =
this.glassParts[`${part.glassLocation}-${part.glassName}`]; this.glassParts[`${part.glassLocation}-${part.glassName}`];
const selectedPartData = part.parts.filter( const selectedPartData = part.parts.filter(
(part) => (part) =>
selectedPartForGlassLocationAndName && selectedPartForGlassLocationAndName &&
part.partNumber == selectedPartForGlassLocationAndName?.partNumber part.partNumber == selectedPartForGlassLocationAndName?.partNumber
)[0]; )[0];
if (selectedPartData) matchedParts.push(selectedPartData); if (selectedPartData) matchedParts.push(selectedPartData);
}); });
return matchedParts; return matchedParts;
}, },
PartsOrQuestions() { PartsOrQuestions() {
const partsData = this.PartsFromApi; const partsData = this.PartsFromApi;
// Map API result data, to vehicle-parts data structure // Map API result data, to vehicle-parts data structure
const mappedData = partsData.partsOrQuestions.map((g) => { const mappedData = partsData.partsOrQuestions.map((g) => {
return { return {
glassName: g.glassName, glassName: g.glassName,
glassLocation: g.glassLocation, glassLocation: g.glassLocation,
colorAnswers: g.parts.reduce((arr, p) => { colorAnswers: g.parts.reduce((arr, p) => {
arr.push({ arr.push({
ColorAnswerText: p.color, ColorAnswerText: p.color,
FeatureAnswers: [ FeatureAnswers: [
{ {
FeatureAnswerText: FeatureAnswerText:
p.description === "" ? p.color : p.description, p.description === "" ? p.color : p.description,
PartNumber: p.partNumber, PartNumber: p.partNumber,
}, },
], ],
}); });
return arr; return arr;
}, []), }, []),
}; };
}); });
return mappedData; return mappedData;
}, },
PartsFromApi() { PartsFromApi() {
return store.getters.pageData(fmgPageValues.VEHICLE_PARTS); return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
}, },
RefPrefix() { RefPrefix() {
return "partQuestion"; return "partQuestion";
}, },
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
// Check if isRepair is populated and if the pageData we need is here (Parts data) // Check if isRepair is populated and if the pageData we need is here (Parts data)
if ( if (
store.getters.damage.isRepair != null && store.getters.damage.isRepair != null &&
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS)) Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
.length !== 0 .length !== 0
) { ) {
return true; return true;
} }
return false; return false;
}, },
backButtonAction() { backButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
async forwardButtonAction() { async forwardButtonAction() {
const selectedGlassPartNumbers = []; const selectedGlassPartNumbers = [];
const matchedParts = []; const matchedParts = [];
// Compile all selected parts from the page. // Compile all selected parts from the page.
for (let [key, value] of Object.entries(this.glassParts)) { for (let [key, value] of Object.entries(this.glassParts)) {
for (let [glassKey, glassValue] of Object.entries(value)) { for (let [glassKey, glassValue] of Object.entries(value)) {
selectedGlassPartNumbers.push(glassValue[0]); selectedGlassPartNumbers.push(glassValue[0]);
} }
} }
// Match them to the parts from the API. // Match them to the parts from the API.
for (let [key, value] of Object.entries(this.PartsFromApi.partsOrQuestions)) { for (let [key, value] of Object.entries(
for (let [partKey, partValue] of Object.entries(value.parts)) { this.PartsFromApi.partsOrQuestions
const currentPart = )) {
this.PartsFromApi.partsOrQuestions[key].parts[partKey]; for (let [partKey, partValue] of Object.entries(value.parts)) {
const isMatched = selectedGlassPartNumbers.some( const currentPart =
(p) => p === currentPart.partNumber this.PartsFromApi.partsOrQuestions[key].parts[partKey];
); const isMatched = selectedGlassPartNumbers.some(
(p) => p === currentPart.partNumber
);
if (isMatched) { if (isMatched) {
matchedParts.push(currentPart); matchedParts.push(currentPart);
} }
} }
} }
// If no parts could be matched, throw an error. // If no parts could be matched, throw an error.
if (this.isForwardActionDisabled) { if (this.isForwardActionDisabled) {
this.$refs.funnelFooter.removeLoader(); this.$refs.funnelFooter.removeLoader();
throw new Error("Could not match any parts to the selected parts"); throw new Error("Could not match any parts to the selected parts");
} }
// Save parts to the store. // Save parts to the store.
await this.dispatchStoreAction( await this.dispatchStoreAction(
storeActions.SAVE_GLASS_PARTS, storeActions.SAVE_GLASS_PARTS,
matchedParts matchedParts
); );
// Navigate to the next page. // Navigate to the next page.
this.$router.navigate( this.$router.navigate(
this.navigationScenarios.SELECTED_PARTS, this.navigationScenarios.SELECTED_PARTS,
this.$route this.$route
); );
}, },
LoadInitialPartsData() { LoadInitialPartsData() {
const partsData = this.PartsFromApi; const partsData = this.PartsFromApi;
const alreadyPopulatedPartsData = const alreadyPopulatedPartsData =
this.$store.getters.lineItems.glassParts === null this.$store.getters.lineItems.glassParts === null
? {} ? {}
: this.$store.getters.lineItems.glassParts; : this.$store.getters.lineItems.glassParts;
partsData.partsOrQuestions.map((g) => { partsData.partsOrQuestions.map((g) => {
// If the part is already populated, use the value from the store and populate the v-model. // If the part is already populated, use the value from the store and populate the v-model.
Object.keys(alreadyPopulatedPartsData).forEach((key) => { Object.keys(alreadyPopulatedPartsData).forEach((key) => {
const partNumber = alreadyPopulatedPartsData[key].partNumber; const partNumber = alreadyPopulatedPartsData[key].partNumber;
g.parts.forEach((p) => { g.parts.forEach((p) => {
if (p.partNumber === partNumber) { if (p.partNumber === partNumber) {
this.glassParts[g.glassLocation + "-" + g.glassName] = { this.glassParts[g.glassLocation + "-" + g.glassName] = {
[g.glassLocation]: [partNumber], [g.glassLocation]: [partNumber],
}; };
} }
}); });
}); });
}); });
}, },
}, },
mounted() { mounted() {
this.LoadInitialPartsData(); this.LoadInitialPartsData();
}, },
}; };
</script> </script>