commit
38234728be
9 changed files with 153 additions and 56 deletions
|
|
@ -1,8 +1,5 @@
|
|||
<template>
|
||||
<div class="textbox-question" :class="[
|
||||
errors.length > 0 ? 'has-error' : '',
|
||||
hasError ? 'has-error' : '',
|
||||
]">
|
||||
<div class="textbox-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
|
||||
<label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label>
|
||||
<input v-model="value"
|
||||
v-maska="mask"
|
||||
|
|
|
|||
7
src/constants/dynamic-strings.js
Normal file
7
src/constants/dynamic-strings.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
const dynamicStrings = {
|
||||
GLOBAL_STATE: "globalState",
|
||||
CUSTOM: "custom",
|
||||
ROUTER_LINK: "routerLink"
|
||||
};
|
||||
|
||||
export { dynamicStrings };
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import store from "@/store";
|
||||
import { dynamicStrings } from "../constants/dynamic-strings";
|
||||
|
||||
export function fetchCmsContentForPage(fmgPage) {
|
||||
return store
|
||||
|
|
@ -40,12 +41,15 @@ export function fetchCmsContentForPage(fmgPage) {
|
|||
function mapStringToState(str) {
|
||||
// Pull all matches out of the string.
|
||||
const regexExp = new RegExp("{(.*?):(.*?)}", "g");
|
||||
const matches = [...str.matchAll(regexExp)];
|
||||
const regexMatches = [...str.matchAll(regexExp)];
|
||||
const globalStateMatches = regexMatches.filter(match => {
|
||||
return match[1] === dynamicStrings.GLOBAL_STATE;
|
||||
})
|
||||
|
||||
// Our final string value that will be built from the matches.
|
||||
let stringBuilder = "";
|
||||
|
||||
for (const match of matches) {
|
||||
for (const match of globalStateMatches) {
|
||||
// Reset store state for each match.
|
||||
let storeState = store.state;
|
||||
|
||||
|
|
@ -60,7 +64,7 @@ function mapStringToState(str) {
|
|||
const stringWithReplacement = str.replace(match[0], storeState);
|
||||
|
||||
// If we still have values we need to substitute, call this function again.
|
||||
if (stringWithReplacement.includes("{globalState:")) {
|
||||
if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) {
|
||||
return mapStringToState(stringWithReplacement);
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +100,7 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
|
|||
function processWidgetItemForReplacement(widgetModel, key) {
|
||||
// If we have a string, and it needs to be replaced.
|
||||
if (typeof widgetModel[key] === "string") {
|
||||
if (widgetModel[key].includes("{globalState:")) {
|
||||
if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
|
||||
widgetModel[key] = mapStringToState(widgetModel[key]);
|
||||
}
|
||||
return widgetModel[key];
|
||||
|
|
@ -116,4 +120,4 @@ function processWidgetItemForReplacement(widgetModel, key) {
|
|||
|
||||
// If we have something else like a number, boolean, etc. just return it
|
||||
return widgetModel[key];
|
||||
}
|
||||
}
|
||||
5
src/helpers/damage-helper.js
Normal file
5
src/helpers/damage-helper.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import store from "@/store";
|
||||
|
||||
export function getDamageString() {
|
||||
return store.getters.damage.glassToReplace.length > 1 ? "match" : store.getters.damage.glassToReplace[0].location;
|
||||
}
|
||||
15
src/helpers/damage-helper.spec.js
Normal file
15
src/helpers/damage-helper.spec.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import {getDamageString} from "./damage-helper";
|
||||
|
||||
jest.mock("@/store", () => ({
|
||||
getters: {damage: {
|
||||
glassToReplace: [{location: "TEST"}]
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
describe("damage-helper.js", () => {
|
||||
it("Should return damage getter info", () => {
|
||||
const damage = getDamageString();
|
||||
expect(damage).toEqual("TEST")
|
||||
});
|
||||
});
|
||||
|
|
@ -26,7 +26,8 @@
|
|||
</div>
|
||||
<alert
|
||||
class="my-3"
|
||||
cmsWidgetName="NoServiceZipWidget"
|
||||
:manualHeadline="NoServiceZipHeader"
|
||||
:manualCopy="NoServiceZipBody"
|
||||
v-if="newServiceZipRequired"
|
||||
alertClass="alert-danger"
|
||||
/>
|
||||
|
|
@ -38,7 +39,8 @@
|
|||
/>
|
||||
<alert
|
||||
class="my-3"
|
||||
cmsWidgetName="MatchedDifferentVehicleAlertWidget"
|
||||
:manualHeadline="MatchedDifferentVehicleAlertHeader"
|
||||
:manualCopy="MatchedDifferentVehicleAlertBody"
|
||||
v-if="vinDoesNotMatchCarId"
|
||||
alertClass="alert-warning"
|
||||
/>
|
||||
|
|
@ -75,8 +77,10 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { getDamageString } from "@/helpers/damage-helper";
|
||||
import { required, regex } from "@/helpers/validation-rules";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
||||
|
|
@ -118,8 +122,38 @@ export default {
|
|||
zip: '',
|
||||
email: '',
|
||||
serviceZip: '',
|
||||
customAlertData: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
MatchedDifferentVehicleAlertHeader(){
|
||||
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "HeadlineText");
|
||||
// Replace all instances where custom data is implemented
|
||||
text = text.replaceAll("{custom:damage}", getDamageString());
|
||||
|
||||
return text;
|
||||
},
|
||||
MatchedDifferentVehicleAlertBody(){
|
||||
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "BodyText");
|
||||
// Replace all instances where custom data is implemented
|
||||
text = text.replaceAll("{custom:damage}", getDamageString());
|
||||
text = text.replaceAll("{custom:plateLookupYear}", this.customAlertData?.vehicleInfo?.year);
|
||||
text = text.replaceAll("{custom:plateLookupMake}", this.customAlertData?.vehicleInfo?.make);
|
||||
text = text.replaceAll("{custom:plateLookupModel}", this.customAlertData?.vehicleInfo?.model);
|
||||
|
||||
return text;
|
||||
},
|
||||
NoServiceZipHeader(){
|
||||
let text = this.getCmsContent("NoServiceZipWidget", "HeadlineText");
|
||||
// Replace all instances where custom data is implemented
|
||||
text = text.replaceAll("{custom:zip}", this.zip);
|
||||
|
||||
return text;
|
||||
},
|
||||
NoServiceZipBody(){
|
||||
return this.getCmsContent("NoServiceZipWidget", "BodyText");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.vehicle.carId !== null;
|
||||
|
|
@ -137,6 +171,7 @@ export default {
|
|||
async forwardButtonAction() {
|
||||
const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.zip);
|
||||
if (!zipValidation.data.isServiceable) {
|
||||
this.customAlertData.zip = this.zip;
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
this.newServiceZipRequired = true;
|
||||
return;
|
||||
|
|
@ -146,34 +181,50 @@ export default {
|
|||
this.vinNotValid = true;
|
||||
return;
|
||||
});
|
||||
if (vinLookup.data.vehicle.carId !== store.getters.vehicle.carId) {
|
||||
if (vinLookup.data.vehicle.carId !== store.getters.vehicle.carId && !this.vinDoesNotMatchCarId) {
|
||||
this.customAlertData.vehicleInfo = vinLookup.data.vehicle;
|
||||
this.$refs.funnelFooter.updateButtonText(`Continue with ${vinLookup.data.vin} ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`);
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
this.vinDoesNotMatchCarId = true;
|
||||
return;
|
||||
//return;
|
||||
}
|
||||
|
||||
let vin;
|
||||
let vehicleInfo;
|
||||
let glassOptions;
|
||||
|
||||
if(this.vinDoesNotMatchCarId) {
|
||||
vin = vinLookup.data.vin;
|
||||
vehicleInfo = vinLookup.data.vehicle;
|
||||
glassOptions = store.dispatch(storeActions.GET_DAMAGE_OPTIONS, { carId: vinLookup.data.vehicle.carId }).data;
|
||||
} else {
|
||||
vin = store.getters.vehicle.vin;
|
||||
vehicleInfo = store.getters.vehicle;
|
||||
glassOptions = store.getters.damage.glassToReplace;
|
||||
}
|
||||
|
||||
//this.updateCustomerInfo(vin, vehicleInfo, zipValidation.data.state);
|
||||
|
||||
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
|
||||
this.storeActions.GET_PARTS_OR_QUESTIONS,
|
||||
{
|
||||
carId: store.getters.vehicle.carId,
|
||||
carId: vehicleInfo.carId,
|
||||
glassArray: store.getters.damage.glassToReplace,
|
||||
zipCode: this.zip,
|
||||
vin: vinLookup.vin
|
||||
zipCode: this.serviceZip ? this.serviceZip : this.zip,
|
||||
vin: vin
|
||||
},
|
||||
false
|
||||
);
|
||||
this.navigateForward(partsData);
|
||||
console.log(partsData)
|
||||
//this.navigateForward(partsData);
|
||||
},
|
||||
navigateForward(partsData){
|
||||
if(partsData.data.partsOrQuestions[0].partQuestions && partsData.data.partsOrQuestions[0].partQuestions.length > 0){
|
||||
if(partsData.data){
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_PARTS_QUESTION, this.$route, {}, {}, partsData.data);
|
||||
return;
|
||||
} else if((!partsData.data.partsOrQuestions[0].partQuestions || partsData.data.partsOrQuestions[0].partQuestions.length < 1) && partsData.data.partsOrQuestions[0].parts.length > 1) {
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data);
|
||||
return;
|
||||
} else {
|
||||
this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_SINGLE_PART, this.$route);
|
||||
navigateToHeritageFunnel();
|
||||
return;
|
||||
}
|
||||
},
|
||||
validateZip(zip) {
|
||||
|
|
@ -188,25 +239,25 @@ export default {
|
|||
{ licensePlate: plate, licenseState: state }
|
||||
);
|
||||
},
|
||||
updateStore() {
|
||||
updateCustomerInfo(vin, vehicleInfo, registrationState) {
|
||||
// if(vehicleDamage){
|
||||
// store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
// }
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
|
||||
store.commit(storeMutations.UPDATE_YEAR, null);
|
||||
store.commit(storeMutations.UPDATE_MAKE, null);
|
||||
store.commit(storeMutations.UPDATE_MODEL, null);
|
||||
store.commit(storeMutations.UPDATE_STYLE, null);
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, null);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null);
|
||||
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, null);
|
||||
store.commit(storeMutations.UPDATE_REGISTRATION_STATE, null);
|
||||
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, null);
|
||||
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, null);
|
||||
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, null);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_VIN, vin);
|
||||
store.commit(storeMutations.UPDATE_YEAR, vehicleInfo.year);
|
||||
store.commit(storeMutations.UPDATE_MAKE, vehicleInfo.make);
|
||||
store.commit(storeMutations.UPDATE_MODEL, vehicleInfo.model);
|
||||
store.commit(storeMutations.UPDATE_STYLE, vehicleInfo.style);
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, vehicleInfo.carId);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, vehicleInfo.category);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, vehicleInfo.imageUrl);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, vehicleInfo.imageVifNumber);
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, vehicleInfo.imageColor);
|
||||
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, this.licensePlate);
|
||||
store.commit(storeMutations.UPDATE_REGISTRATION_STATE, registrationState);
|
||||
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, this.zip);
|
||||
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, this.serviceZip);
|
||||
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.email);
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<template>
|
||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage="false" />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||
<h1>Part Questions Page Placeholder</h1>
|
||||
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget" @back-clicked="backButtonAction"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -49,18 +51,7 @@ export default {
|
|||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.$refs.funnelHeader.initializeComponent(
|
||||
resultMap.cmsContent.FunnelHeaderWidget
|
||||
);
|
||||
vm.$refs.vehicleBanner.initializeComponent(
|
||||
resultMap.cmsContent.VehicleBannerWidget
|
||||
);
|
||||
vm.$refs.funnelSubHeader.initializeComponent(
|
||||
resultMap.cmsContent.FunnelSubHeaderWidget
|
||||
);
|
||||
vm.$refs.funnelFooter.initializeComponent(
|
||||
resultMap.cmsContent.FunnelFooterWidget
|
||||
);
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import alert from "./alert";
|
||||
|
||||
describe("alert.vue", () => {
|
||||
|
|
@ -9,6 +9,13 @@ describe("alert.vue", () => {
|
|||
propsData: {
|
||||
isDismissible: true
|
||||
},
|
||||
computed: {
|
||||
splitAlertCopyForLink: {
|
||||
get() {
|
||||
return "TEST";
|
||||
},
|
||||
}
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
|
|
@ -24,6 +31,13 @@ describe("alert.vue", () => {
|
|||
propsData: {
|
||||
alertClass: 'warning'
|
||||
},
|
||||
computed: {
|
||||
splitAlertCopyForLink: {
|
||||
get() {
|
||||
return "TEST";
|
||||
},
|
||||
}
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@
|
|||
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]"
|
||||
>
|
||||
<p class="m-0 fw-bold small alert-heading">{{ alertHeadline }}</p>
|
||||
<p class="m-0 text-body small">{{ alertCopy }}</p>
|
||||
<p v-if="splitAlertCopyForLink.length">
|
||||
<template v-for="copy in splitAlertCopyForLink" :key="copy">
|
||||
<span v-if="copy.includes('routerLink:')" class="m-0 text-body small">
|
||||
<router-link :to="{query: {fmgPage: `${copy.split(':')[1].split(',')[0]}`}, name: 'root'}">{{ copy.split(':')[1].split(',')[1] }}</router-link>
|
||||
</span>
|
||||
<span v-else class="m-0 text-body small">{{ copy }}</span>
|
||||
</template>
|
||||
</p>
|
||||
<p v-else class="m-0 text-body small">{{ alertCopy }}</p>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close p-2"
|
||||
|
|
@ -26,6 +34,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "alert",
|
||||
props: {
|
||||
|
|
@ -49,6 +58,10 @@ export default {
|
|||
alertCopy(){
|
||||
return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'BodyText') : this.manualCopy;
|
||||
},
|
||||
splitAlertCopyForLink(){
|
||||
// Splits content when brackets are found in text so that text can be looped through and router-link can be injected when needed
|
||||
return this.alertCopy.split(/{(.*?)}/g);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue