CSR-731 | Final copy w/o tests
This commit is contained in:
parent
65cf2d3b84
commit
5e3abe4ebf
8 changed files with 235 additions and 231 deletions
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue