CSR-731 | Working version before refactor
This commit is contained in:
parent
a8d9060b22
commit
a60398a55b
10 changed files with 488 additions and 116 deletions
|
|
@ -52,7 +52,8 @@
|
|||
:selectOnKeypress="selectOnKeypress"
|
||||
@blur="handleBlur"
|
||||
@focus="handleFocus"
|
||||
@buttonClicked="handleAnswerChange" />
|
||||
@buttonClicked="handleAnswerChange"
|
||||
:additionalData="additionalData" />
|
||||
<!-- For nested questions -->
|
||||
<transition name="fade" mode="out-in">
|
||||
<div
|
||||
|
|
@ -121,6 +122,7 @@ export default {
|
|||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
additionalData: null
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -157,7 +159,7 @@ export default {
|
|||
classes = "ui-radio d-flex";
|
||||
break;
|
||||
case "servicePackageRadio":
|
||||
classes = "ui-radio d-flex service-package";
|
||||
classes = "package-main";
|
||||
break;
|
||||
}
|
||||
return classes;
|
||||
|
|
@ -165,10 +167,12 @@ export default {
|
|||
getComponentWrapperClasses() {
|
||||
let classes = "";
|
||||
|
||||
classes += this.isWid ? "col-12" : "col";
|
||||
classes += this.isWide ? "col-12" : "col";
|
||||
|
||||
if (this.buttonType == "radio") {
|
||||
classes += " radio-button-container";
|
||||
} else if (this.buttonType == "servicePackageRadio") {
|
||||
classes = "package-wrapper";
|
||||
}
|
||||
|
||||
return classes;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
const dynamicStrings = {
|
||||
GLOBAL_STATE: "globalState",
|
||||
CUSTOM: "custom",
|
||||
ROUTER_LINK: "routerLink:"
|
||||
ROUTER_LINK: "routerLink:",
|
||||
TEXT_LINK: "textLink:",
|
||||
};
|
||||
|
||||
export { dynamicStrings };
|
||||
|
|
@ -46,22 +46,16 @@ function mapStringToState(str) {
|
|||
return match[1] === dynamicStrings.GLOBAL_STATE;
|
||||
})
|
||||
|
||||
|
||||
// Our final string value that will be built from the matches.
|
||||
let stringBuilder = "";
|
||||
|
||||
for (const match of globalStateMatches) {
|
||||
// Reset store state for each match.
|
||||
let storeState = store.state;
|
||||
|
||||
for (const s of match[2].split(".")) {
|
||||
if (storeState[s] != undefined) {
|
||||
storeState = storeState[s];
|
||||
} else {
|
||||
return ""; // if we can't map our string to state data, return an empty string.
|
||||
}
|
||||
const valueFromStore = getStoreValueFromString(match[2]);
|
||||
if (!valueFromStore) {
|
||||
return ""; // if we can't map our string to state data, return an empty string.
|
||||
}
|
||||
|
||||
const stringWithReplacement = str.replace(match[0], storeState);
|
||||
const stringWithReplacement = str.replace(match[0], valueFromStore);
|
||||
|
||||
// If we still have values we need to substitute, call this function again.
|
||||
if (stringWithReplacement.includes(dynamicStrings.GLOBAL_STATE)) {
|
||||
|
|
@ -71,7 +65,6 @@ function mapStringToState(str) {
|
|||
// Concatenate the string.
|
||||
stringBuilder = `${stringBuilder} ${stringWithReplacement}`;
|
||||
}
|
||||
|
||||
return stringBuilder.trimStart();
|
||||
}
|
||||
|
||||
|
|
@ -100,10 +93,11 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
|
|||
function processWidgetItemForReplacement(widgetModel, key) {
|
||||
// If we have a string, and it needs to be replaced.
|
||||
if (typeof widgetModel[key] === "string") {
|
||||
widgetModel[key] = checkForIfStatements(widgetModel[key], dynamicStrings.GLOBAL_STATE, getStoreValueFromString);
|
||||
if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
|
||||
widgetModel[key] = mapStringToState(widgetModel[key]);
|
||||
}
|
||||
return widgetModel[key];
|
||||
return reconstructPageLevelIfStatements(widgetModel[key]);
|
||||
}
|
||||
|
||||
// If we have an object. array, etc
|
||||
|
|
@ -122,6 +116,89 @@ function processWidgetItemForReplacement(widgetModel, key) {
|
|||
return widgetModel[key];
|
||||
}
|
||||
|
||||
export function checkForIfStatements(str, ifConditionString, replaceStringCallback) {
|
||||
// Pattern to split off each {if:...}, {else}, {end} and their trailing strings
|
||||
const regexExp = new RegExp("(?<cleanString>^.+?)(?=(?:{if))|(?<ifOperator>{if:)(?<ifConditionType>.*?:)(?<ifCondition>.*?)}(?<ifTrailingString>.*?)(?=(?:{(?:end|else|if:.*?)}))|(?<elseOperator>{else})(?<elseTrailingString>.*?)(?=(?:{(?:end|else|if:.*?)}))|(?<endOperator>{end})(?<endTrailingString>.*?)(?=(?:{(?:end|else|if:.*?)})|$)", "g");
|
||||
const regexMatches = [...str.matchAll(regexExp)];
|
||||
// Find and evaluate full globalState if statements
|
||||
if (regexMatches.length) {
|
||||
regexMatches.forEach((match, index) => {
|
||||
if(match.groups.ifOperator) {
|
||||
if(regexMatches[index+1].groups.endOperator) {
|
||||
processIfStatement(regexMatches.slice(index, index+2), ifConditionString, replaceStringCallback) + regexMatches[index+1].groups.endTrailingString;
|
||||
}
|
||||
else if(regexMatches[index+2].groups.endOperator) {
|
||||
processIfStatement(regexMatches.slice(index, index+3), ifConditionString, replaceStringCallback)+ regexMatches[index+2].groups.endTrailingString;
|
||||
}
|
||||
}
|
||||
});
|
||||
const processedString = joinProcessedRegexArray(regexMatches);
|
||||
return checkForIfStatements(processedString, ifConditionString, replaceStringCallback);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function reconstructPageLevelIfStatements(str) {
|
||||
return str.replaceAll("(((", "{").replaceAll(")))","}");
|
||||
}
|
||||
|
||||
function joinProcessedRegexArray(regexMatches) {
|
||||
let processedString = "";
|
||||
regexMatches.forEach((match) => {
|
||||
processedString += match.groups.cleanString ?? match[0];
|
||||
});
|
||||
return processedString;
|
||||
}
|
||||
|
||||
function processIfStatement(ifStatementArray, ifConditionString, replaceStringCallback) {
|
||||
if(ifStatementArray[0].groups.ifConditionType.includes(ifConditionString)) {
|
||||
//replace the if condition with a real value
|
||||
const ifCondition = replaceStringCallback(ifStatementArray[0].groups.ifCondition);
|
||||
//convert the entire if statement
|
||||
let processedIfStatementString;
|
||||
if (ifCondition) {
|
||||
processedIfStatementString = ifStatementArray[0].groups.ifTrailingString;
|
||||
} else {
|
||||
processedIfStatementString = ifStatementArray[1].groups.elseTrailingString ?? "";
|
||||
}
|
||||
ifStatementArray.forEach((entry) => {
|
||||
if (entry.groups.ifOperator) {
|
||||
entry.groups.cleanString = processedIfStatementString;
|
||||
} else if (entry.groups.endOperator) {
|
||||
entry.groups.cleanString = entry.groups.endTrailingString;
|
||||
} else {
|
||||
entry.groups.cleanString = "";
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// stow the if statement to reconstruct later when passing to the page
|
||||
ifStatementArray.forEach((entry) => {
|
||||
const groups = entry.groups;
|
||||
if (groups.ifOperator)
|
||||
groups.cleanString = "(((if:" + groups.ifConditionType + groups.ifCondition + ")))" + groups.ifTrailingString;
|
||||
else if (groups.elseOperator)
|
||||
groups.cleanString = "(((else)))" + groups.elseTrailingString;
|
||||
else
|
||||
groups.cleanString = "(((end)))" + groups.endTrailingString;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getStoreValueFromString(str) {
|
||||
let storeOrStateObject = str.includes('getters') ? store :store.state;
|
||||
for (const s of str.split(".")) {
|
||||
if (storeOrStateObject[s] != undefined) {
|
||||
storeOrStateObject = storeOrStateObject[s];
|
||||
} else {
|
||||
return ""; // if we can't map our string to state data, return an empty string.
|
||||
}
|
||||
}
|
||||
return storeOrStateObject;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export function doesCopyContainRouterLink(copy) {
|
||||
return copy.includes(this.dynamicStrings.ROUTER_LINK);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
<alert
|
||||
v-if="displayNonServiceableZipAlert"
|
||||
class="my-3"
|
||||
cmsWidgetName="AlertNonServiceableZipWidget"
|
||||
:manualHeadline="AlertNonServiceableZipHeader"
|
||||
:manualCopy="AlertNonServiceableZipBody"
|
||||
alertClass="alert-danger"
|
||||
|
|
|
|||
|
|
@ -20,16 +20,17 @@
|
|||
<cashOrInsuranceQuestion
|
||||
ref="cashOrInsurance"
|
||||
cmsWidgetName="CashOrInsuranceQuestionWidget"
|
||||
v-model="isInsurance"
|
||||
v-model="isInsuranceSelected"
|
||||
groupName="CashOrInsuranceQuestion"
|
||||
/>
|
||||
<radioServicePackage/>
|
||||
<servicePackageQuestion
|
||||
ref="servicePackage"
|
||||
cashCmsWidgetName="CashServicePackageQuestionWidget"
|
||||
insuranceCmsWidgetName="InsuranceServicePackageWidget"
|
||||
insuranceCmsWidgetName="InsuranceServicePackageQuestionWidget"
|
||||
v-model="selectedPackage"
|
||||
groupName="ServicePackageQuestion"
|
||||
:lineItems="lineItems"
|
||||
:isInsuranceSelected="isInsuranceSelected"
|
||||
/>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
|
|
@ -125,7 +126,6 @@ import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
|||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||
import radioServicePackage from "@/ux-components/radio-service-package/radio-service-package";
|
||||
import cashOrInsuranceQuestion from "./cash-or-insurance-question/cash-or-insurance-question";
|
||||
import servicePackageQuestion from "./service-package-question/service-package-question";
|
||||
import textBlock from "@/common-components/text-block/text-block";
|
||||
|
|
@ -156,34 +156,68 @@ export default {
|
|||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
console.log("cmsContent", cmsContent);
|
||||
vm.setCmsContent(cmsContent);
|
||||
vm.isInsurance = vm.getDefaultIsInsuranceValue();
|
||||
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue();
|
||||
vm.selectedPackage = vm.getDefaultSelectedPackageValue();
|
||||
vm.lineItems = [
|
||||
{
|
||||
PartNumber : "001",
|
||||
Description : "Windshield with Recal",
|
||||
PartType : "Windshield",
|
||||
Quantity : "1",
|
||||
BasePartNumber : "001",
|
||||
Color : "Green",
|
||||
CanSafeliteRecalibrate: true,
|
||||
Price : 350.00
|
||||
},
|
||||
{
|
||||
PartNumber : "002",
|
||||
Description : "Front Wipers",
|
||||
PartType : "frontWiper",
|
||||
Quantity : "1",
|
||||
Price : 35.00,
|
||||
},
|
||||
{
|
||||
PartNumber : "003",
|
||||
Description : "Rear Wiper",
|
||||
PartType : "rearWiper",
|
||||
Quantity : "1",
|
||||
Price : 25.00,
|
||||
},
|
||||
{
|
||||
PartNumber : "004",
|
||||
Description : "Rain Defense",
|
||||
PartType : "rainDefense",
|
||||
Quantity : "1",
|
||||
Price : 30.00,
|
||||
},
|
||||
{
|
||||
PartNumber : "005",
|
||||
Description : "Recalibration",
|
||||
PartType : "recalibration",
|
||||
Quantity : "1",
|
||||
Price : 150.00,
|
||||
},
|
||||
];
|
||||
});
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
cashOrInsuranceThreshhold: 600,
|
||||
pricingRequestResult: null,
|
||||
isInsurance: null,
|
||||
isInsuranceSelected: null,
|
||||
selectedPackage: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selectedPackage(newPackage, oldPackage) {
|
||||
console.log("new package: ", newPackage);
|
||||
console.log("old package: ", oldPackage);
|
||||
lineItems: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.order.damage.isRepair || (store.getters.order.lineItems?.glassParts != null && store.getters.order.lineItems.glassParts.length > 0);
|
||||
},
|
||||
getDefaultIsInsuranceValue() {
|
||||
var defaultIsInsuranceValue = this.$store.getters.order.payment.isInsurance;
|
||||
if(defaultIsInsuranceValue != null) {
|
||||
return defaultIsInsuranceValue;
|
||||
getDefaultIsInsuranceSelectedValue() {
|
||||
const defaultIsInsuranceSelectedValue = this.$store.getters.order.payment.isInsurance;
|
||||
if(defaultIsInsuranceSelectedValue != null) {
|
||||
return defaultIsInsuranceSelectedValue;
|
||||
} else {
|
||||
return this.economyPackagePrice > this.cashOrInsuranceThreshhold;
|
||||
}
|
||||
|
|
@ -194,16 +228,6 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
years() {
|
||||
return ["2011", "2012"];
|
||||
},
|
||||
answersFromCms() {
|
||||
return ["Pay on my own", "Pay with insurance"];
|
||||
},
|
||||
economyPackagePrice() {
|
||||
//TODO build out the pricing logic CSR-504
|
||||
return 0;
|
||||
},
|
||||
selectedChipCountValues: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
|
|
@ -219,7 +243,6 @@ export default {
|
|||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
Form,
|
||||
radioServicePackage,
|
||||
textBlock,
|
||||
cashOrInsuranceQuestion,
|
||||
servicePackageQuestion,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
<template>
|
||||
<div class="service-package-question">
|
||||
<buttonQuestion
|
||||
:answers="cashAnswersFromCms"
|
||||
:groupName="groupName"
|
||||
buttonType="servicePackageRadio"
|
||||
v-model="selectedValues"
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
<buttonQuestion
|
||||
v-if="!isInsuranceSelected"
|
||||
:answers="cashAnswersFromCms"
|
||||
: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>
|
||||
|
|
@ -20,22 +29,33 @@ export default ({
|
|||
groupName: String,
|
||||
cashCmsWidgetName: String,
|
||||
insuranceCmsWidgetName: String,
|
||||
isInsuranceSelected: Boolean,
|
||||
lineItems: null,
|
||||
},
|
||||
computed: {
|
||||
cashAnswersFromCms(){
|
||||
let cashAnswers = "";
|
||||
let cmsContent = this.getCmsContent(this.cashCmsWidgetName, 'Answers');
|
||||
const cmsContent = this.getCmsContent(this.cashCmsWidgetName, 'Answers');
|
||||
if (cmsContent) {
|
||||
cashAnswers = cmsContent?.map((x) => ({
|
||||
buttonLabel : x.Name,
|
||||
value : x.SubWidgetName
|
||||
value : x.SubWidgetName,
|
||||
}));
|
||||
}
|
||||
|
||||
return cashAnswers;
|
||||
},
|
||||
insuranceAnswersFromCms() {
|
||||
return this.getCmsContent(this.insuranceCmsWidgetName, 'Answers');
|
||||
// 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() {
|
||||
|
|
@ -45,9 +65,15 @@ export default ({
|
|||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
},
|
||||
additionalData() {
|
||||
return {
|
||||
lineItems: this.lineItems,
|
||||
isInsuranceSelected: this.isInsuranceSelected
|
||||
};
|
||||
},
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,27 +1,50 @@
|
|||
<template>
|
||||
<baseInputButton
|
||||
v-if="shouldDisplayThisPackage"
|
||||
v-bind="$props"
|
||||
buttonWrapperClasses="ui-radio form-check test000"
|
||||
inputClasses="form-check-input"
|
||||
@buttonClicked="handleAnswerChange">
|
||||
<div class="d-flex align-items-start form-check-label">
|
||||
<p v-if="buttonLabel" class="m-0">{{ buttonLabel }}</p>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{
|
||||
screenReaderOnlyText
|
||||
}}</span>
|
||||
<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>
|
||||
<p class="sub-label m-0" v-if="this.getSubHeaderTextFromCms" v-html="this.getSubHeaderTextFromCms"></p>
|
||||
<!-- Parse the body text containing <ul> with logic -->
|
||||
<ul>
|
||||
<li v-for="listItem in this.arrayOfListItemsFromBodyText" :key="listItem">
|
||||
<!-- no textLink -->
|
||||
<span v-if="!doesCopyContainTextLink(listItem)" v-html="listItem"></span>
|
||||
<!-- with textLink -->
|
||||
<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" />
|
||||
</span>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- End of body text parsing -->
|
||||
<div class="package-footer" v-if="getFooterTextFromCms" v-html="getFooterTextFromCms"></div>
|
||||
</div>
|
||||
</div>
|
||||
</baseInputButton>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
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, checkForIfStatements } from "@/helpers/cms-content-helper";
|
||||
import { dynamicStrings } from "@/constants/dynamic-strings";
|
||||
|
||||
export default {
|
||||
name: "servicePackageRadio",
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
components: {
|
||||
baseInputButton,
|
||||
textLink,
|
||||
},
|
||||
computed: {
|
||||
getHeaderTextFromCms(){
|
||||
|
|
@ -31,63 +54,269 @@ export default {
|
|||
return this.getCmsContent(this.value, 'SubheaderText');
|
||||
},
|
||||
getBodyTextFromCms(){
|
||||
return this.getCmsContent(this.value, 'BodyText');
|
||||
const bodyText = this.getCmsContent(this.value, 'BodyText');
|
||||
return this.checkForIfStatements(bodyText, "custom", this.getCustomValueFromString);
|
||||
},
|
||||
getFooterTextFromCms(){
|
||||
return this.getCmsContent(this.value, 'FooterText');
|
||||
const footerText = this.getCmsContent(this.value, 'FooterText');
|
||||
return this.checkForIfStatements(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);
|
||||
},
|
||||
isRecalibrationOnOrder() {
|
||||
return this.lineItemsContainsPartType("recalibration");
|
||||
},
|
||||
frontWipersApplicableForStandard() {
|
||||
const frontWipersAreAvailable = this.lineItemsContainsPartType("frontWiper");
|
||||
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("rearWiper");
|
||||
return this.glassToReplaceContainsGlassLocation("Rear") && rearWiperIsAvailable;
|
||||
},
|
||||
frontWipersApplicableForPremium() {
|
||||
const frontWipersAreAvailable = this.lineItemsContainsPartType("frontWiper");
|
||||
return frontWipersAreAvailable;
|
||||
},
|
||||
rearWiperApplicableForPremium() {
|
||||
const rearWiperIsAvailable = this.lineItemsContainsPartType("rearWiper");
|
||||
return this.glassToReplaceContainsGlassLocation("Rear") && rearWiperIsAvailable;
|
||||
},
|
||||
rainDefenseApplicableForPremium() {
|
||||
const frontWipersAreAvailable = this.lineItemsContainsPartType("frontWiper");
|
||||
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: {
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
checkForIfStatements,
|
||||
stripUlTagFromCopy(copy) {
|
||||
const regex = /(?:<ul(?:.*?)>)|(?:<\/ul>)/g;
|
||||
return copy.replace(regex, '');
|
||||
},
|
||||
getArrayOfListItemsFromRawCmsCopy(copy) {
|
||||
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 === 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 === "frontWiper") || (priceRearWipers && item.PartType === "rearWiper")) {
|
||||
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 === "frontWiper") || (priceRearWipers && item.PartType === "rearWiper") || (priceRainDefense && item.PartType === "rainDefense")) {
|
||||
vapsPrice += item.Price;
|
||||
}
|
||||
});
|
||||
return vapsPrice;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.form-check {
|
||||
position: relative;
|
||||
.package-main {
|
||||
.package-wrapper {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
.form-check-input {
|
||||
border: 1px solid $gray-500;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.5rem;
|
||||
opacity: 1;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
&:checked {
|
||||
background-color: $white;
|
||||
background-size: 71%;
|
||||
background-position: center;
|
||||
border: 1px solid $blue;
|
||||
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 50 50' xml:space='preserve'%3e%3ccircle cx='25' cy='25' r='25' fill='%231574A1'/%3e%3c/svg%3e");
|
||||
+ .form-check-label {
|
||||
p {
|
||||
font-weight: 500;
|
||||
font-size: 0.875rem;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
|
||||
&,
|
||||
& + .form-check-label {
|
||||
margin-top: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.form-check-input {
|
||||
box-shadow: 0 0 0 4px $blue-300;
|
||||
input[type="radio"] {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
|
||||
+ .package-label {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: 1px solid $gray-300;
|
||||
box-shadow: 0px 4px 8px -4px rgba(0, 0, 0, 0.15), 0px 4px 24px -8px rgba(0, 0, 0, 0.2);
|
||||
border-radius: .5rem;
|
||||
overflow: hidden;
|
||||
min-height: 58px;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: relative;
|
||||
top: 5px;
|
||||
margin-right: 1rem;
|
||||
border-radius: 50%;
|
||||
border: 1px solid $gray-500;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 19px;
|
||||
top: 24px;
|
||||
border-radius: 50%;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
+ .package-label {
|
||||
&:before {
|
||||
border: 1px solid #8E9292;
|
||||
box-shadow: 0px 0px 0px 4px #9FCEE6, 0px 1px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:checked {
|
||||
+ .package-label {
|
||||
.package-specs {
|
||||
max-height: 1000px;
|
||||
}
|
||||
}
|
||||
+ .package-label {
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
max-height: 500px;
|
||||
}
|
||||
+ .package-label {
|
||||
&:before {
|
||||
box-shadow: 0px 0px 0px 1px $blue;
|
||||
}
|
||||
}
|
||||
+ .package-label {
|
||||
&:after {
|
||||
background: $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
+ .package-label {
|
||||
&:before {
|
||||
border: 2px solid $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p {
|
||||
font-weight: 400;
|
||||
font-size: 0.875rem;
|
||||
color: $gray-600;
|
||||
|
||||
.package-specs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
max-height: 0;
|
||||
transition: all .75s ease;
|
||||
|
||||
p {
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
span.pricing-info {
|
||||
color: $green;
|
||||
}
|
||||
&.sub-label {
|
||||
color: $green;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
margin: 1rem 0 0 -15px;
|
||||
padding: 0;
|
||||
li {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -56,6 +56,14 @@ export default {
|
|||
isServiceable: serviceZipValidationResponse.data.isServiceable,
|
||||
state: serviceZipValidationResponse.data.state
|
||||
};
|
||||
},
|
||||
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);
|
||||
return totalPrice;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ export default {
|
|||
valueToLogType: String,
|
||||
validationRules: String,
|
||||
isWide: Boolean,
|
||||
isRequired: Boolean
|
||||
isRequired: Boolean,
|
||||
additionalData: null,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -31,7 +32,6 @@ export default {
|
|||
if (this.preHandleAnswerChange) {
|
||||
this.preHandleAnswerChange(e)
|
||||
}
|
||||
|
||||
this.$emit("change", e);
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ const getDefaultState = () => {
|
|||
glassToReplace: null,
|
||||
partQuestionAnswers: null,
|
||||
moldingQuestionAnswers: null,
|
||||
capabilityQuestionAnswers: null
|
||||
capabilityQuestionAnswers: null,
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: null
|
||||
|
|
@ -372,6 +372,10 @@ export const getters = {
|
|||
},
|
||||
eventBus: (state) => state.applicationUser.eventBus,
|
||||
damage: (state) => state.order.damage,
|
||||
hasAnyNonWindshieldGlassParts: (state) => {
|
||||
const nonWindshieldItems = state.order.damage.glassToReplace.filter(glassToReplace => glassToReplace.glassLocation != "Windshield");
|
||||
return !!nonWindshieldItems.length;
|
||||
},
|
||||
lineItems: (state) => state.order.lineItems,
|
||||
pageData: (state) => (page) => { return state.applicationUser.pageData[page]; },
|
||||
applicationUser: (state) => state.applicationUser,
|
||||
|
|
@ -1133,7 +1137,6 @@ function getHasRecalibrationPart(state) {
|
|||
} else { // Does not have 'requiresRecalibration'
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue