CSR-731 | Final copy w/o tests

This commit is contained in:
Scott Kiener 2022-10-24 11:26:42 -04:00
parent 65cf2d3b84
commit 5e3abe4ebf
8 changed files with 235 additions and 231 deletions

View file

@ -40,6 +40,9 @@
:is="buttonType"
:buttonLabel="answer.buttonLabel"
:buttonLabelSubCopy="answer.buttonLabelSubCopy"
:buttonBodyCopy="answer.buttonBodyCopy"
:buttonAuxillaryCopy="answer.buttonAuxillaryCopy"
:buttonFooterCopy="answer.buttonFooterCopy"
:buttonImage="answer.buttonImage"
:buttonImageId="answer.buttonImageId"
:groupName="answer.groupName"
@ -49,7 +52,7 @@
:isWide="isWide"
:validationRules="validationRules"
:textPosition="textPosition"
:additionalData="additionalData" />
:additionalData="additionalData"
:lastValuePushedToGa="lastValuePushedToGa"
:setLastValuePushedToGa="setLastValuePushedToGa"
v-model="selectedValues"
@ -187,6 +190,9 @@ export default {
answer.altText ?? (answer.Name ? answer.Name : answer),
buttonLabelSubCopy:
answer.buttonLabelSubCopy ?? answer.SubText,
buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy,
buttonAuxillaryCopy: answer.buttonAuxillaryCopy ?? answer.buttonAuxillaryCopy,
buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy,
buttonImage: answer.buttonImage ?? answer.AnswerImageUrl,
buttonImageId: answer.buttonImageId ?? answer.ImageId,
groupName: this.formatString(this.groupName),

View file

@ -40,7 +40,6 @@ export function fetchCmsContentForPage(fmgPage) {
// Function to convert a string, into a matching global state item.
function mapStringToState(str) {
// Pull all matches out of the string.
//const regexExp = new RegExp("{(.*?):(.*?)}", "g");
const regexExp = new RegExp("{([^{}]*?):([^{}]*?)}", "g");
const regexMatches = [...str.matchAll(regexExp)];
const globalStateMatches = regexMatches.filter(match => {
@ -147,32 +146,38 @@ export function processIfStatements(str, ifConditionKeyword, replacePlaceholderC
} else {
const ifStatementRegexExpression = getIfStatementRegexExpression();
const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)];
const completeIfStatementArray = getFirstNonNestedIfStatement(ifStatementRegexMatches, ifConditionKeyword);
const processedIfStatementString = executeIfStatementAndGetProcessedString(completeIfStatementArray, replacePlaceholderCallback);
replaceIfStatementWithProcessedString(completeIfStatementArray, processedIfStatementString);
const completeIfStatementArray = getAndFlagFirstNonNestedIfStatementWithKeyword(ifStatementRegexMatches, ifConditionKeyword);
executeIfStatementAndSetProcessedStrings(completeIfStatementArray, replacePlaceholderCallback);
const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches);
return processIfStatements(reconstructedPostProcessedString, ifConditionKeyword, replacePlaceholderCallback);
}
}
function getFirstNonNestedIfStatement(matches, ifConditionKeyword) {
function getAndFlagFirstNonNestedIfStatementWithKeyword(matches, ifConditionKeyword) {
let index = 0;
for(const match of matches) {
if (match.groups.ifOperator && match.groups.ifConditionType === ifConditionKeyword) {
if (match.groups.isIfStatement && match.groups.ifConditionType === ifConditionKeyword) {
let interiorIndex = 0;
let nestedLevel = 0;
let elseStatementIndex = null;
for (const interiorMatch of matches.slice(index+1)) {
if (interiorMatch.groups.ifOperator) {
if (interiorMatch.groups.isIfStatement) {
if(interiorMatch.groups.ifConditionType === ifConditionKeyword) {
break;
} else {
nestedLevel++;
}
} else if (interiorMatch.groups.endOperator) {
} else if(interiorMatch.groups.isElseStatement) {
if (!nestedLevel) {
elseStatementIndex = interiorIndex+1;
}
} else if (interiorMatch.groups.isEndStatement) {
if (nestedLevel) {
nestedLevel--;
} else {
return matches.slice(index, index + interiorIndex+2);
const ifStatementArray = matches.slice(index, index + interiorIndex+2);
flagMatchesForProcessing(ifStatementArray, elseStatementIndex);
return ifStatementArray;
}
}
interiorIndex++;
@ -182,33 +187,51 @@ function getFirstNonNestedIfStatement(matches, ifConditionKeyword) {
}
}
function flagMatchesForProcessing(matches, elseStatementIndex) {
matches[0].isFlaggedForProcessing = true;
matches[matches.length-1].isFlaggedForProcessing = true;
if (elseStatementIndex) {
matches[elseStatementIndex].isFlaggedForProcessing = true;
}
}
function joinProcessedRegexArray(regexMatches) {
let processedString = "";
regexMatches.forEach((match) => {
processedString += match.groups.cleanString ?? match[0];
const rawString = match[0];
processedString += match.groups.processedString ?? rawString;
});
return processedString;
}
function executeIfStatementAndGetProcessedString(ifStatementArray, replacePlaceholderCallback) {
function executeIfStatementAndSetProcessedStrings(ifStatementArray, replacePlaceholderCallback) {
const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition);
if (ifCondition) {
return ifStatementArray[0].groups.ifTrailingString;
} else {
return ifStatementArray[1].groups.elseTrailingString ?? "";
}
let isInsideDesiredBlock = ifCondition;
ifStatementArray.forEach(entry => {
if (entry.groups.isElseStatement && entry.isFlaggedForProcessing) {
isInsideDesiredBlock = !ifCondition;
} else if (entry.groups.isEndStatement && entry.isFlaggedForProcessing) {
isInsideDesiredBlock = true;
}
setProcessedStringOnEntry(entry, isInsideDesiredBlock);
});
}
function replaceIfStatementWithProcessedString(ifStatementArray, processedIfStatementString) {
ifStatementArray.forEach((entry) => {
if (entry.groups.ifOperator) {
entry.groups.cleanString = processedIfStatementString;
} else if (entry.groups.endOperator) {
entry.groups.cleanString = entry.groups.endTrailingString;
function setProcessedStringOnEntry(entry, isInsideDesiredBlock) {
if (!isInsideDesiredBlock) {
entry.groups.processedString = "";
} else {
if (entry.groups.isIfStatement) {
entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.ifTrailingString : entry[0];
} else if (entry.groups.isElseStatement) {
entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.elseTrailingString : entry[0];
} else {
entry.groups.cleanString = "";
entry.groups.processedString = entry.isFlaggedForProcessing ? entry.groups.endTrailingString : entry[0];
}
})
}
}
function getIfStatementRegexExpression() {
@ -217,23 +240,23 @@ function getIfStatementRegexExpression() {
const anyLogicOperatorNonCapture = "(?:{(?:end|else|if:.*?)})";
// NOTE: ?<variableName> syntax stores the captured match like so: match.groups.variableName
const matchStartOfString = (
"(?<cleanString>^.+?)" + // Match and Capture all characters (lazy), cannot be empty
"(?<processedString>^.+?)" + // Match and Capture all characters (lazy), cannot be empty
"(?=(?:{if))" // Looks ahead but does not capture {if
);
const matchIfOperator = (
"(?<ifOperator>{if:)" + // Match & Capture {if:
"(?<isIfStatement>{if:)" + // Match & Capture {if:
"(?<ifConditionType>.*?):" + // Match all chars up to and including next ":" - Capture all chars up to ":"
"(?<ifCondition>.*?)}" + // Match all chars up to and including next "}" - Capture all chars up to "}"
"(?<ifTrailingString>.*?)" + // Match and Capture all characters (lazy), can be empty
"(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator
);
const matchElseOperator = (
"(?<elseOperator>{else})" + // Match & Capture {else}
"(?<isElseStatement>{else})" + // Match & Capture {else}
"(?<elseTrailingString>.*?)" + // Match & Capture all characters (lazy), can be empty
"(?=" + anyLogicOperatorNonCapture + ")" // Looks ahead but does not capture the next logic operator
);
const matchEndOperator = (
"(?<endOperator>{end})" + // Match & Capture {end}
"(?<isEndStatement>{end})" + // Match & Capture {end}
"(?<endTrailingString>.*?)" + // Match & Capture all chracters (lazy), can be empty
"(?="+ anyLogicOperatorNonCapture + "|$)" // Looks ahead but does not capture the next logic operator
);
@ -249,19 +272,23 @@ export function doesCopyContainRouterLink(copy) {
return copy.includes(this.dynamicStrings.ROUTER_LINK);
}
export function doesCopyContainTextLink(copy) {
return copy.includes(dynamicStrings.TEXT_LINK);
}
export function splitCopyOnCMSPlaceHolder(copy){
// splits copy on { ... } such as {routerlink: ...}
return copy.split(/{(.*?)}/g);
}
export function getRouterLinkRouteFromCopy(copy){
export function getLinkTargetFromCopy(copy){
// sample input: {routerLink:estimate,provide your VIN}
// first split would return 'estimate,provide your VIN'
// second split would return 'estimate'
return copy.split(':')[1].split(',')[0];
}
export function getRouterLinkDisplayTextFromCopy(copy){
export function getLinkDisplayTextFromCopy(copy){
// sample input: {routerLink:estimate,provide your VIN}
// first split would return 'estimate,provide your VIN'
// second split would return 'provide your VIN'

View file

@ -29,7 +29,7 @@
<div class="alert-provide-vin my-3" v-if="splitAlertProvideVinBodyForLink.length">
<span v-for="copy in splitAlertProvideVinBodyForLink" :key="copy">
<span v-if="doesCopyContainRouterLink(copy)" class="text-body">
<router-link :to="{query: {fmgPage: `${getRouterLinkRouteFromCopy(copy)}`}, name: 'root'}">{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link>
<router-link :to="{query: {fmgPage: `${getLinkTargetFromCopy(copy)}`}, name: 'root'}">{{ getLinkDisplayTextFromCopy(copy) }}</router-link>
</span>
<span v-else class="m-0 text-body" v-html="copy"></span>
</span>
@ -67,8 +67,8 @@ import { Form, defineRule } from "vee-validate";
import { isGlassAvailableForCarId } from "@/helpers/damage-helper";
import { doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy, } from "@/helpers/cms-content-helper";
getLinkTargetFromCopy,
getLinkDisplayTextFromCopy, } from "@/helpers/cms-content-helper";
import { routerParams } from "@/router/router-constants/router-params";
import vinPagesMixin from "@/mixins/vin-pages-mixin";
@ -148,8 +148,8 @@ export default {
methods: {
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
getLinkTargetFromCopy,
getLinkDisplayTextFromCopy,
arePagePrerequisitesValid() {
if (
store.getters.order.vehicle.carId

View file

@ -1,26 +1,18 @@
<template>
<buttonQuestion
v-if="!isInsuranceSelected"
:answers="cashAnswersFromCms"
:answers="servicePackageAnswers"
:groupName="groupName"
buttonType="servicePackageRadio"
v-model="selectedValues"
isRequired
:additionalData="additionalData"
/>
<buttonQuestion
v-if="isInsuranceSelected"
:answers="insuranceAnswersFromCms"
:groupName="groupName"
buttonType="servicePackageRadio"
v-model="selectedValues"
isRequired
:additionalData="additionalData"
/>
</template>
<script>
import buttonQuestion from "@/common-components/button-question/button-question";
import baseMixin from "@/mixins/base-mixin.js";
import { splitCopyOnCMSPlaceHolder, processIfStatements } from "@/helpers/cms-content-helper";
import { damageLocationsSelected as glassLocation } from "@/constants/damage-locations-selected";
export default ({
name: "servicePackageQuestion",
@ -33,30 +25,6 @@ export default ({
lineItems: null,
},
computed: {
cashAnswersFromCms(){
let cashAnswers = "";
const cmsContent = this.getCmsContent(this.cashCmsWidgetName, 'Answers');
if (cmsContent) {
cashAnswers = cmsContent?.map((x) => ({
buttonLabel : x.Name,
value : x.SubWidgetName,
}));
}
return cashAnswers;
},
// I'm starting to think there is no reason to have a separate insurance package answers
// the only benefit would be
insuranceAnswersFromCms() {
let insuranceAnswers = "";
const cmsContent = this.getCmsContent(this.insuranceCmsWidgetName, 'Answers');
if (cmsContent) {
insuranceAnswers = cmsContent?.map((x) => ({
buttonLabel : x.Name,
value : x.SubWidgetName,
}));
}
return insuranceAnswers;
},
selectedValues: {
get: function() {
return this.modelValue;
@ -65,11 +33,140 @@ export default ({
this.$emit("update:modelValue", newValue);
}
},
additionalData() {
return {
lineItems: this.lineItems,
isInsuranceSelected: this.isInsuranceSelected
};
servicePackageAnswers() {
const cmsWidgetName = this.isInsuranceSelected ? this.insuranceCmsWidgetName : this.cashCmsWidgetName;
let cmsAnswersContent = this.getCmsContent(cmsWidgetName, 'Answers');
if (!this.shouldDisplayStandardPackage) {
cmsAnswersContent = cmsAnswersContent.filter(answer => answer.Name != "Standard");
}
const modifiedAnswers = cmsAnswersContent.map(answer => ({
value: answer.Name,
buttonLabel : this.getHeaderTextFromCms(answer.SubWidgetName),
buttonLabelSubCopy: this.getSubheaderTextFromCms(answer.SubWidgetName),
buttonBodyCopy: this.getBodyTextFromCms(answer.SubWidgetName),
buttonAuxillaryCopy: this.getPackagePriceString(answer.Name),
buttonFooterCopy: this.getFooterTextFromCms(answer.SubWidgetName),
}));
return modifiedAnswers;
},
isRecalibrationOnOrder() {
return this.lineItemsContainsPartNumber("RECAL");
},
frontWipersApplicableForStandard() {
const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER");
const isRepair = this.$store.getters.order.damage.isRepair;
const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation(glassLocation.WINDSHIELD);
if (frontWipersAreAvailable) {
if (isRepair) {
return true;
} else {
if (glassToReplaceContainsWindshield) {
return true;
} else {
return false;
}
}
} else {
return false;
}
},
rearWiperApplicableForStandard() {
const rearWiperIsAvailable = this.lineItemsContainsPartType("REAR WIPER");
return this.glassToReplaceContainsGlassLocation(glassLocation.REAR) && rearWiperIsAvailable;
},
frontWipersApplicableForPremium() {
const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER");
return frontWipersAreAvailable;
},
rearWiperApplicableForPremium() {
const rearWiperIsAvailable = this.lineItemsContainsPartType("REAR WIPER");
return this.glassToReplaceContainsGlassLocation(glassLocation.REAR) && rearWiperIsAvailable;
},
rainDefenseApplicableForPremium() {
const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER");
const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation(glassLocation.WINDSHIELD);
const glassToReplaceContainsRearGlass = this.glassToReplaceContainsGlassLocation(glassLocation.REAR);
if(this.frontWipersApplicableForStandard) {
return true;
} else if (!frontWipersAreAvailable) {
return true;
} else if (!glassToReplaceContainsWindshield && !glassToReplaceContainsRearGlass) {
return true;
} else {
return false;
}
},
shouldDisplayStandardPackage() {
return this.frontWipersApplicableForStandard || this.rearWiperApplicableForStandard;
}
},
methods: {
splitCopyOnCMSPlaceHolder,
processIfStatements,
getHeaderTextFromCms(cmsWidgetName) {
return this.getCmsContent(cmsWidgetName, 'HeaderText');
},
getSubheaderTextFromCms(cmsWidgetName) {
return this.getCmsContent(cmsWidgetName, 'SubheaderText');
},
getBodyTextFromCms(cmsWidgetName) {
const bodyText = this.getCmsContent(cmsWidgetName, 'BodyText');
return this.processIfStatements(bodyText, "custom", this.getCustomValueFromString);
},
getFooterTextFromCms(cmsWidgetName){
const footerText = this.getCmsContent(cmsWidgetName, 'FooterText');
return this.processIfStatements(footerText, "custom", this.getCustomValueFromString);
},
getPackagePriceString(packageName) {
const formattedPriceFloat = parseFloat(this.getPackagePrice(packageName)).toFixed(2);
return (this.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
},
getPackagePrice(packageName) {
let priceFloat = this.isInsuranceSelected ? 0 : baseMixin.methods.getEconomyPackagePrice(this.lineItems);
if (packageName === "Standard") {
priceFloat += this.getStandardPackageVapsPrice();
} else if (packageName === "Premium") {
priceFloat += this.getPremiumPackageVapsPrice();
}
return priceFloat;
},
getStandardPackageVapsPrice() {
let vapsPrice = 0;
const priceFrontWipers = this.frontWipersApplicableForStandard;
const priceRearWipers = this.rearWiperApplicableForStandard;
this.lineItems.forEach(item => {
if ((priceFrontWipers && item.partType.toUpperCase().includes("FRONT WIPER")) || (priceRearWipers && item.partType.toUpperCase().includes("REAR WIPER"))) {
vapsPrice += item.price;
}
});
return vapsPrice;
},
getPremiumPackageVapsPrice() {
let vapsPrice = 0;
const priceFrontWipers = this.frontWipersApplicableForPremium;
const priceRearWipers = this.rearWiperApplicableForPremium;
const priceRainDefense = this.rainDefenseApplicableForPremium;
this.lineItems.forEach(item => {
if ((priceFrontWipers && item.partType.toUpperCase().includes("FRONT WIPER")) || (priceRearWipers && item.partType.toUpperCase().includes("REAR WIPER")) || (priceRainDefense && item.partType.toUpperCase().includes("RAIN DEFENSE"))) {
vapsPrice += item.price;
}
});
return vapsPrice;
},
getCustomValueFromString(str) {
return this[str];
},
lineItemsContainsPartType(partType) {
const partTypeMatches = this.lineItems.filter(lineItem => lineItem.partType.toUpperCase().includes(partType));
return !!partTypeMatches.length;
},
lineItemsContainsPartNumber(partNumber) {
const partNumberMatches = this.lineItems.filter(lineItem => lineItem.partNumber.toUpperCase().includes(partNumber));
return !!partNumberMatches.length;
},
glassToReplaceContainsGlassLocation(glassLocation) {
const glassLocationMatches = this.$store.getters.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.glassLocation === glassLocation);
return !!glassLocationMatches.length;
},
},
components: {

View file

@ -1,15 +1,14 @@
<template>
<baseInputButton
v-if="shouldDisplayThisPackage"
v-bind="$props"
@buttonClicked="handleAnswerChange">
<div class="package-label" for="testradio">
<div class="package-specs">
<p :class="[this.getSubHeaderTextFromCms ? 'mb-2' : 'm-0']">
<span v-html="this.getHeaderTextFromCms"></span>
<span class="pricing-info" v-html="getPackagePriceString"></span>
<p :class="[this.buttonLabelSubCopy ? 'mb-2' : 'm-0']">
<span v-html="this.buttonLabel"></span>
<span class="pricing-info" v-html="this.buttonAuxillaryCopy"></span>
</p>
<p class="sub-label m-0" v-if="this.getSubHeaderTextFromCms" v-html="this.getSubHeaderTextFromCms"></p>
<p class="sub-label m-0" v-if="this.buttonLabelSubCopy" v-html="this.buttonLabelSubCopy"></p>
<!-- Parse the body text containing <ul> with logic -->
<ul>
<li v-for="listItem in this.arrayOfListItemsFromBodyText" :key="listItem">
@ -19,13 +18,13 @@
<template v-else v-for="copy in splitCopyOnCMSPlaceHolder(listItem)" :key="copy">
<span v-if="!doesCopyContainTextLink(copy)" v-html="copy"></span>
<span v-else>
<textLink linkType="dashedUnderline" :text="getTextLinkDisplayTextFromCopy(copy)" href="#!" data-bs-toggle="modal" :data-bs-target="'#' + getTextAreaTargetFromCopy(copy)" aria-label="Modal window" />
<textLink linkType="dashedUnderline" :text="getLinkDisplayTextFromCopy(copy)" href="#!" data-bs-toggle="modal" :data-bs-target="'#' + getLinkTargetFromCopy(copy)" aria-label="Modal window" />
</span>
</template>
</li>
</ul>
<!-- End of body text parsing -->
<div class="package-footer" v-if="getFooterTextFromCms" v-html="getFooterTextFromCms"></div>
<div class="package-footer" v-if="this.buttonFooterCopy" v-html="this.buttonFooterCopy"></div>
</div>
</div>
</baseInputButton>
@ -35,9 +34,10 @@
import baseInputButton from "@/common-components/base-input-button/base-input-button";
import textLink from "@/ux-components/text-link/text-link";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
import baseMixin from "@/mixins/base-mixin.js";
import { splitCopyOnCMSPlaceHolder, processIfStatements } from "@/helpers/cms-content-helper";
import { dynamicStrings } from "@/constants/dynamic-strings";
import { getLinkDisplayTextFromCopy,
getLinkTargetFromCopy,
splitCopyOnCMSPlaceHolder,
doesCopyContainTextLink } from "@/helpers/cms-content-helper";
export default {
name: "servicePackageRadio",
@ -47,87 +47,15 @@ export default {
textLink,
},
computed: {
getHeaderTextFromCms(){
return this.getCmsContent(this.value, 'HeaderText');
},
getSubHeaderTextFromCms(){
return this.getCmsContent(this.value, 'SubheaderText');
},
getBodyTextFromCms(){
const bodyText = this.getCmsContent(this.value, 'BodyText');
return this.processIfStatements(bodyText, "custom", this.getCustomValueFromString);
},
getFooterTextFromCms(){
const footerText = this.getCmsContent(this.value, 'FooterText');
return this.processIfStatements(footerText, "custom", this.getCustomValueFromString);
},
getPackagePriceString() {
const formattedPriceFloat = parseFloat(this.getPackagePrice()).toFixed(2);
return (this.additionalData.isInsuranceSelected ? "As little as $" : "$") + formattedPriceFloat;
},
arrayOfListItemsFromBodyText() {
return this.getArrayOfListItemsFromRawCmsCopy(this.getBodyTextFromCms);
return this.getArrayOfListItemsFromRawCmsCopy(this.buttonBodyCopy);
},
isRecalibrationOnOrder() {
return this.lineItemsContainsPartType("RECALIBRATION");
},
frontWipersApplicableForStandard() {
const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER");
const isRepair = this.$store.getters.order.damage.isRepair;
const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation("Windshield");
if (frontWipersAreAvailable) {
if (isRepair) {
return true;
} else {
if (glassToReplaceContainsWindshield) {
return true;
} else {
return false;
}
}
} else {
return false;
}
},
rearWiperApplicableForStandard() {
const rearWiperIsAvailable = this.lineItemsContainsPartType("REAR WIPER");
return this.glassToReplaceContainsGlassLocation("Rear") && rearWiperIsAvailable;
},
frontWipersApplicableForPremium() {
const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER");
return frontWipersAreAvailable;
},
rearWiperApplicableForPremium() {
const rearWiperIsAvailable = this.lineItemsContainsPartType("REAR WIPER");
return this.glassToReplaceContainsGlassLocation("Rear") && rearWiperIsAvailable;
},
rainDefenseApplicableForPremium() {
const frontWipersAreAvailable = this.lineItemsContainsPartType("FRONT WIPER");
const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation("Windshield");
const glassToReplaceContainsRearGlass = this.glassToReplaceContainsGlassLocation("Rear");
if(this.frontWipersApplicableForStandard) {
return true;
} else if (!frontWipersAreAvailable) {
return true;
} else if (!glassToReplaceContainsWindshield && !glassToReplaceContainsRearGlass) {
return true;
} else {
return false;
}
},
shouldDisplayThisPackage() {
if (this.value != "StandardServicePackage") {
return true;
} else if (this.frontWipersApplicableForStandard || this.rearWiperApplicableForStandard) {
return true;
} else {
return false;
}
}
},
methods: {
getLinkDisplayTextFromCopy,
getLinkTargetFromCopy,
splitCopyOnCMSPlaceHolder,
processIfStatements,
doesCopyContainTextLink,
stripUlTagFromCopy(copy) {
const regex = /(?:<ul(?:.*?)>)|(?:<\/ul>)/g;
return copy.replace(regex, '');
@ -136,64 +64,6 @@ export default {
const withoutUlTags = this.stripUlTagFromCopy(copy);
return withoutUlTags.split(/(?:<li(?:.*?)>)|(?:<\/li>)/g).filter(lineItem => lineItem);
},
doesCopyContainTextLink(copy) {
return copy.includes(dynamicStrings.TEXT_LINK);
},
getTextAreaTargetFromCopy(copy){
// sample input: {textLink:estimate,provide your VIN}
// first split would return 'estimate,provide your VIN'
// second split would return 'estimate'
return copy.split(':')[1].split(',')[0];
},
getTextLinkDisplayTextFromCopy(copy){
// sample input: {textLink:estimate,provide your VIN}
// first split would return 'estimate,provide your VIN'
// second split would return 'provide your VIN'
return copy.split(':')[1].split(',')[1];
},
getCustomValueFromString(str) {
return this[str];
},
lineItemsContainsPartType(partType) {
const results = this.additionalData.lineItems.filter(lineItem => lineItem.partType.toUpperCase().includes(partType));
return !!results.length;
},
glassToReplaceContainsGlassLocation(glassLocation) {
const glassLocationMatches = this.$store.getters.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.glassLocation === glassLocation);
return !!glassLocationMatches.length;
},
getPackagePrice() {
let priceFloat = this.additionalData.isInsuranceSelected ? 0 : baseMixin.methods.getEconomyPackagePrice(this.additionalData.lineItems);
if (this.value === "StandardServicePackage") {
priceFloat += this.getStandardPackageVapsPrice();
} else if (this.value === "PremiumServicePackage") {
priceFloat += this.getPremiumPackageVapsPrice();
}
return priceFloat;
},
getStandardPackageVapsPrice() {
let vapsPrice = 0;
const priceFrontWipers = this.frontWipersApplicableForStandard;
const priceRearWipers = this.rearWiperApplicableForStandard;
this.additionalData.lineItems.forEach(item => {
if ((priceFrontWipers && item.partType.toUpperCase().includes("FRONT WIPER")) || (priceRearWipers && item.partType.toUpperCase().includes("REAR WIPER"))) {
vapsPrice += item.price;
}
});
return vapsPrice;
},
getPremiumPackageVapsPrice() {
let vapsPrice = 0;
const priceFrontWipers = this.frontWipersApplicableForPremium;
const priceRearWipers = this.rearWiperApplicableForPremium;
const priceRainDefense = this.rainDefenseApplicableForPremium;
this.additionalData.lineItems.forEach(item => {
if ((priceFrontWipers && item.partType.toUpperCase().includes("FRONT WIPER")) || (priceRearWipers && item.partType.toUpperCase().includes("REAR WIPER")) || (priceRainDefense && item.partType.toUpperCase().includes("RAIN DEFENSE"))) {
vapsPrice += item.price;
}
});
return vapsPrice;
},
},
};
</script>

View file

@ -62,11 +62,13 @@ export default {
};
},
getEconomyPackagePrice(lineItems) {
const nonVapsItems = lineItems.filter((lineItem) => {
return lineItem.PartType != "frontWiper" && lineItem.PartType != "rearWiper" && lineItem.PartType != "rainDefense";
});
let totalPrice = 0;
nonVapsItems.forEach(item => totalPrice += item.price);
lineItems.forEach((lineItem) => {
const partType = lineItem.partType.toUpperCase();
if (!partType.includes("FRONT WIPER") && !partType.includes("REAR WIPER") && !partType.includes("RAIN DEFENSE")) {
totalPrice += lineItem.price;
}
});
return totalPrice;
}
},

View file

@ -9,6 +9,9 @@ export default {
...inputButtonProps,
buttonLabel: [Number, String],
buttonLabelSubCopy: String,
buttonBodyCopy: String,
buttonAuxillaryCopy: String,
buttonFooterCopy: String,
buttonImage: String,
altText: {
type: String,
@ -17,7 +20,6 @@ export default {
textPosition: String,
screenReaderOnlyText: String,
isWide: Boolean,
additionalData: null,
},
computed: {
selectedValue: {

View file

@ -11,7 +11,7 @@
<template v-for="copy in splitCopyOnCMSPlaceHolder(paragraph)" :key="copy">
<span v-if="!doesCopyContainRouterLink(copy)" v-html="copy"></span>
<span v-else>
<router-link :to="{query: {fmgPage: `${getRouterLinkRouteFromCopy(copy)}`}, name: 'root'}">{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link>
<router-link :to="{query: {fmgPage: `${getLinkTargetFromCopy(copy)}`}, name: 'root'}">{{ getLinkDisplayTextFromCopy(copy) }}</router-link>
</span>
</template>
</p>
@ -38,8 +38,8 @@
<script>
import { doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
getLinkTargetFromCopy,
getLinkDisplayTextFromCopy,
splitCMSCopyOnParagraphTag } from "@/helpers/cms-content-helper"
export default {
@ -84,8 +84,8 @@ export default {
methods: {
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy,
getLinkTargetFromCopy,
getLinkDisplayTextFromCopy,
ensureAlertIsInViewPort() {
if (this.shouldScrollToOnMount && this.$el.style.display != 'none') {
var footerHeight = this.getFooterInfoBoxHeight();