Just a draft update

This commit is contained in:
David Back 2023-04-04 12:43:07 -04:00
parent 279076b547
commit d15798ce89
28 changed files with 1161 additions and 124 deletions

View file

@ -14,5 +14,5 @@
@import "@/styles/common-error-styles.scss";
@import "@/styles/common-animations.scss";
@import "@/styles/shared-input-button-styles.scss";
@import "@/styles/client-customizations.scss"
@import "@/styles/client-customizations.scss";
</style>

View file

@ -1,8 +1,8 @@
const damageCustomLabels = Object.freeze({
MATCH: 'match',
MATCH: 'match',
REAR_WINDOW: 'rear window',
SIDE_WINDOW: 'side window',
WINDSHIELD: 'windshield',
WINDSHIELD: 'windshield'
});
export default damageCustomLabels;

View file

@ -1,9 +1,9 @@
const damageLocationsCms = {
WINDSHIELD: "WINDSHIELD",
SIDEDOOR: "SIDEDOOR",
REARWINDOW: "REARWINDOW",
DRIVERSIDE: "DRIVERSIDE",
PASSENGERSIDE: "PASSENGERSIDE",
WINDSHIELD: "WINDSHIELD",
SIDEDOOR: "SIDEDOOR",
REARWINDOW: "REARWINDOW",
DRIVERSIDE: "DRIVERSIDE",
PASSENGERSIDE: "PASSENGERSIDE"
};
export { damageLocationsCms };

View file

@ -1,21 +1,21 @@
const damageLocationsSelected = {
WINDSHIELD: "Windshield",
SIDEDOOR: "SideDoor",
REARWINDOW: "RearWindow",
REPAIR: "Repair",
REPLACE: "Replace",
DRIVER: "Driver",
PASSENGER: "Passenger",
FRONT: "Front",
REAR: "Rear",
BACK: "Back",
QUARTER: "Quarter",
VENT: "Vent",
SINGLE: "Single",
DRIVERSIDE: "DriverSide",
WINDSHIELD: "Windshield",
SIDEDOOR: "SideDoor",
REARWINDOW: "RearWindow",
REPAIR: "Repair",
REPLACE: "Replace",
DRIVER: "Driver",
PASSENGER: "Passenger",
FRONT: "Front",
REAR: "Rear",
BACK: "Back",
QUARTER: "Quarter",
VENT: "Vent",
SINGLE: "Single",
DRIVERSIDE: "DriverSide",
PASSENGERSIDE: "PassengerSide",
STATIONARY: "Stationary",
SLIDER: "Slider",
STATIONARY: "Stationary",
SLIDER: "Slider"
};
export { damageLocationsSelected };

View file

@ -1,8 +1,8 @@
const dynamicStrings = {
GLOBAL_STATE: "globalState",
CUSTOM: "custom",
ROUTER_LINK: "routerLink:",
MODAL_LINK: "modalLink"
CUSTOM: "custom",
ROUTER_LINK: "routerLink:",
MODAL_LINK: "modalLink"
};
export { dynamicStrings };

View file

@ -18,8 +18,8 @@ const customMappings = {
{ key: "Passenger Back", transformedValue: "passenger side back door" },
{ key: "Passenger Vent", transformedValue: "passenger side vent glass" },
{ key: "Passenger Quarter", transformedValue: "passenger side quarter panel" },
{ key: "Passenger SlideDoor", transformedValue: "passenger side sliding door" },
],
{ key: "Passenger SlideDoor", transformedValue: "passenger side sliding door" }
]
};
// Gets an instance of a string where the dynamic portion of the text {custom:KeyName}

View file

@ -46,7 +46,19 @@ const endpoints = {
GetPartFromCapabilityAnswer: {
url: "/parts/api/v1/parts/part-from-capability-answer",
method: "POST",
},
},
GetWipers: {
url: '/parts/api/v1/parts/wipers',
method: 'GET'
},
GetRainDefense: {
url: '/parts/api/v1/parts/rain-defense',
method: 'GET'
},
GetSupportingItems: {
url: '/parts/api/v1/parts/supporting-items',
method: 'POST'
},
GetVehicle: {
url: "/vehicle/api/v1/vehicle/lookup",
method: "GET",
@ -64,20 +76,20 @@ const endpoints = {
method: "POST",
},
LookupVehicleByVin: {
url: "/vehicle/api/v1/vehicle/lookup",
method: "POST",
url: "/vehicle/api/v1/vehicle/lookup",
method: "POST",
},
LookupVinByAddress: {
url: "/vehicle/api/v1/vehicle/lookup-vin-by-address",
method: "POST",
method: "POST",
},
LookupVinByPlate: {
url: "/vehicle/api/v1/vehicle/lookup-vin-by-plate",
method: "POST",
},
InitializeSession: {
url: "/analytics/api/v1/analytics/initialize",
method: "POST",
url: "/analytics/api/v1/analytics/initialize",
method: "POST",
},
GetExperimentsByUser: {
url: "/analytics/api/v1/analytics/get-experiments",

View file

@ -36,7 +36,7 @@ const errorMessages = {
DAMAGE_OPTION_REQUIRED: "Please select an option",
POLICYHOLDER_FIRST_NAME_REQUIRED: "Please enter the policyholder first name",
POLICYHOLDER_LAST_NAME_REQUIRED: "Please enter the policyholder last name",
POLICYHOLDER_LAST_NAME_REQUIRED: "Please enter the policyholder last name"
};

View file

@ -1,17 +1,17 @@
const globalEvents = {
Categories: {
GLOBAL_ALERT: "GLOBAL_ALERT",
GLOBAL_ALERT: "GLOBAL_ALERT"
},
SubCategories: {
PAGE_NOT_FOUND: "PAGE_NOT_FOUND",
},
PAGE_NOT_FOUND: "PAGE_NOT_FOUND"
}
};
const globalEventTypes = {
Success: "alert-success",
Warning: "alert-warning",
Info: "alert-info",
Danger: "alert-danger",
Danger: "alert-danger"
};
export { globalEvents, globalEventTypes };

View file

@ -1,14 +1,14 @@
const experimentUniverses = {
ISS_FUNNEL: "ISSFunnel",
ISS_FUNNEL: "ISSFunnel"
};
const experimentSettings = {
GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index",
GOOGLE_CUSTOM_DIMENSION_INDEX: "Google Custom Dimension Index"
};
const experimentTriggers = {
SITE_ENTRY: "SiteEntry",
PAGE_ENTRY: "PageEntry",
PAGE_ENTRY: "PageEntry"
};
export { experimentUniverses, experimentSettings, experimentTriggers };

View file

@ -1,3 +1,3 @@
export const headerKeys = {
EXPERIMENT: "X-Experiment-Data",
EXPERIMENT: "X-Experiment-Data"
};

View file

@ -3,8 +3,8 @@
const endpoints = {
GetRouteInfo: {
url: "https://mockey.qa.sagaws.net/service/ISS/Content/RouteInfo",
method: "GET",
},
method: "GET"
}
};

View file

@ -0,0 +1,8 @@
const partTypeStrings = {
FRONT_WIPER: 'FRONT WIPER',
REAR_WIPER: 'REAR WIPER',
RAIN_DEFENSE: 'RAIN DEFENSE',
RECALIBRATION: 'RECALIBRATION'
};
export { partTypeStrings };

View file

@ -49,5 +49,5 @@ export const states = {
WA: "Washington",
WV: "West Virginia",
WI: "Wisconsin",
WY: "Wyoming",
WY: "Wyoming"
};

View file

@ -34,7 +34,7 @@ const tintMap = {
{ name: "gray tint privacy", src: "Glass-NoShade-GrayTint.svg" },
// No shade or tint
{ name: "clear", src: "Glass-NoShade-NoTint.svg" },
{ name: "clear", src: "Glass-NoShade-NoTint.svg" }
],
windshield: [
@ -69,8 +69,8 @@ const tintMap = {
{ name: "gray tint privacy", src: "Windshield-NoShade-GrayTint.svg" },
// No shade or tint
{ name: "clear", src: "Windshield-NoShade-NoTint.svg" },
],
{ name: "clear", src: "Windshield-NoShade-NoTint.svg" }
]
};
// Gets the tint image source string given the glass location, and the tint description (like 'Green Tint')

View file

@ -5,7 +5,7 @@ const vehicleCategories = {
COMMERCIALVAN: "COMMERCIAL VAN",
SUV: "SUV",
MOTORHOME: "MOTOR HOME",
SEMI: "SEMI",
SEMI: "SEMI"
};
export { vehicleCategories };

View file

@ -1,7 +1,7 @@
const vinLookupMethodSelections = Object.freeze({
MANUALVIN: 'ManualVin',
MANUALVIN: 'ManualVin',
LICENSEPLATE: 'LicensePlate',
HOMEADDRESS: 'HomeAddress',
HOMEADDRESS: 'HomeAddress'
});
export {vinLookupMethodSelections};

View file

@ -38,7 +38,7 @@
:buttonLabel="answer.buttonLabel"
:buttonLabelSubCopy="answer.buttonLabelSubCopy"
:buttonBodyCopy="answer.buttonBodyCopy"
:buttonAuxillaryCopy="answer.buttonAuxillaryCopy"
:buttonAuxiliaryCopy="answer.buttonAuxiliaryCopy"
:buttonFooterCopy="answer.buttonFooterCopy"
:buttonImage="answer.buttonImage"
:buttonImageId="answer.buttonImageId"
@ -195,7 +195,7 @@ export default {
altText: answer.altText ?? (answer.Name ? answer.Name : answer),
buttonLabelSubCopy: answer.buttonLabelSubCopy ?? answer.SubText,
buttonBodyCopy: answer.buttonBodyCopy ?? answer.buttonBodyCopy,
buttonAuxillaryCopy: answer.buttonAuxillaryCopy ?? answer.buttonAuxillaryCopy,
buttonAuxiliaryCopy: answer.buttonAuxiliaryCopy ?? answer.buttonAuxiliaryCopy,
buttonFooterCopy: answer.buttonFooterCopy ?? answer.buttonFooterCopy,
buttonImage: answer.buttonImage ?? answer.AnswerImageUrl,
buttonImageId: answer.buttonImageId ?? answer.ImageId,

View file

@ -1,10 +1,10 @@
import { dynamicStrings } from "@/constants/dynamic-strings";
import { dynamicStrings } from '@/constants/dynamic-strings';
import { useMainStore } from '@/store';
export function fetchCmsContentForPage(issPage) {
const store = useMainStore()
const clientName = store.issConfig.clientName;
const accountNumber = store.issConfig.accountNumber;
const store = useMainStore()
const clientName = store.issConfig.clientName;
const accountNumber = store.issConfig.accountNumber;
const clientOverride = (clientName.length > 0 && accountNumber > 0);
return store.getPageData(issPage)
@ -18,7 +18,7 @@ export function fetchCmsContentForPage(issPage) {
}
else {
// Else get the client override page.
const pageName = issPage + "_" + clientName.toLowerCase().replace(/ /g, "");
const pageName = issPage + '_' + clientName.toLowerCase().replace(/ /g, '');
return store.getPageData(pageName)
.then(
@ -140,7 +140,12 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetName) {
// This is a recursive function, it will call itself until it runs out of items to iterate on given the object.
function processWidgetItemForReplacement(widgetModel, key) {
// If we have a string, and it needs to be replaced.
if (typeof widgetModel[key] === "string") {
if (typeof widgetModel[key] === 'string') {
widgetModel[key] = processIfStatements(
widgetModel[key],
dynamicStrings.GLOBAL_STATE,
getStoreValueFromString
);
if (widgetModel[key].includes(dynamicStrings.GLOBAL_STATE)) {
widgetModel[key] = mapStringToState(widgetModel[key]);
}
@ -152,7 +157,7 @@ function processWidgetItemForReplacement(widgetModel, key) {
// If we have an object. array, etc
if (
typeof widgetModel[key] === "object" &&
typeof widgetModel[key] === 'object' &&
Object.keys(widgetModel[key]).length
) {
Object.keys(widgetModel[key]).forEach((item) => {
@ -167,13 +172,13 @@ function processWidgetItemForReplacement(widgetModel, key) {
}
function mapStringToModal(str) {
let startIndex = str.indexOf("{" + dynamicStrings.MODAL_LINK);
let startIndex = str.indexOf('{' + dynamicStrings.MODAL_LINK);
let linkToReplace = str.substring(startIndex, str.length);
linkToReplace = linkToReplace.substring(0, linkToReplace.indexOf("}") + 1);
linkToReplace = linkToReplace.substring(0, linkToReplace.indexOf('}') + 1);
let params = linkToReplace.substring((dynamicStrings.MODAL_LINK).length + 2, linkToReplace.length -1)
let splitParams = params.split(",");
let bodyText = '<a href="#!" data-bs-toggle="modal" data-bs-target="#' + splitParams[0] + '" aria-label="Modal window">' + splitParams[1] +'</a>'
let splitParams = params.split(',');
let bodyText = '<a href="#!" data-bs-toggle="modal" data-bs-target="#' + splitParams[0] + '" aria-label="Modal window">' + splitParams[1] + '</a>';
return str.replace(linkToReplace, bodyText);
}
@ -181,28 +186,22 @@ function mapStringToModal(str) {
// 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 regexMatches = [...str.matchAll(regexExp)];
const regexExp = new RegExp('{(.*?):(.*?)}', 'g');
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 = "";
let stringBuilder = '';
for (const match of globalStateMatches) {
// Reset store state for each match.
let storeState = useMainStore();
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 '';
}
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)) {
@ -216,6 +215,186 @@ function mapStringToState(str) {
return stringBuilder.trimStart();
}
function getStoreValueFromString(str) {
let storeOrStateObject = useMainStore();
for (const s of str.split('.')) {
if (s === 'getters') continue;
if (storeOrStateObject[s] != undefined) {
storeOrStateObject = storeOrStateObject[s];
} else {
return ''
}
}
return storeOrStateObject;
}
///////////////////////////////////
// If Statement Processing Logic //
///////////////////////////////////
/**
* Recursive function - replaces all instances of if statements from the CMS that utilize the specified ifConditionKeyword
* @param {*} str string - Input string to be processed
* @param {*} ifConditionKeyword string - Defines which if statements to process ex: 'globalState'
* @param {*} replacePlaceholderCallback function - Callback to replace CMS placeholder values
* @returns The processed string
*/
export function processIfStatements(str, ifConditionKeyword, replacePlaceholderCallback) {
const containsRelevantIfStatement = new RegExp('{if:' + ifConditionKeyword + ':.+?}', 'g').test(
str
);
if (!containsRelevantIfStatement) {
return str;
} else {
const ifStatementRegexExpression = getIfStatementRegexExpression();
const ifStatementRegexMatches = [...str.matchAll(ifStatementRegexExpression)];
const completeIfStatementArray = getAndFlagFirstNonNestedIfStatementWithKeyword(
ifStatementRegexMatches,
ifConditionKeyword
);
executeIfStatementAndSetProcessedStrings(
completeIfStatementArray,
replacePlaceholderCallback
);
const reconstructedPostProcessedString = joinProcessedRegexArray(ifStatementRegexMatches);
return processIfStatements(
reconstructedPostProcessedString,
ifConditionKeyword,
replacePlaceholderCallback
);
}
}
function getAndFlagFirstNonNestedIfStatementWithKeyword(matches, ifConditionKeyword) {
let index = 0;
for (const match of matches) {
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.isIfStatement) {
if (interiorMatch.groups.ifConditionType === ifConditionKeyword) {
break;
} else {
nestedLevel++;
}
} else if (interiorMatch.groups.isElseStatement) {
if (!nestedLevel) {
elseStatementIndex = interiorIndex + 1;
}
} else if (interiorMatch.groups.isEndStatement) {
if (nestedLevel) {
nestedLevel--;
} else {
const ifStatementArray = matches.slice(index, index + interiorIndex + 2);
flagMatchesForProcessing(ifStatementArray, elseStatementIndex);
return ifStatementArray;
}
}
interiorIndex++;
}
}
index++;
}
}
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) => {
const rawString = match[0];
processedString += match.groups.processedString ?? rawString;
});
return processedString;
}
function executeIfStatementAndSetProcessedStrings(ifStatementArray, replacePlaceholderCallback) {
const ifCondition = replacePlaceholderCallback(ifStatementArray[0].groups.ifCondition);
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 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.processedString = entry.isFlaggedForProcessing
? entry.groups.endTrailingString
: entry[0];
}
}
}
function getIfStatementRegexExpression() {
// Matches but does not capture:
// {if:...} or {else} or {end}
const anyLogicOperatorNonCapture = '(?:{(?:end|else|if:.*?)})';
// NOTE: ?<variableName> syntax stores the captured match like so: match.groups.variableName
const matchStartOfString =
'(?<processedString>^.+?)' + // Match and Capture all characters (lazy), cannot be empty
'(?=(?:{if))'; // Looks ahead but does not capture {if
const matchIfOperator =
'(?<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 =
'(?<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 =
'(?<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
// Combine all matching patterns, separated by 'or' pipes
return new RegExp(
matchStartOfString +
'|' +
matchIfOperator +
'|' +
matchElseOperator +
'|' +
matchEndOperator,
'g'
);
}
//////////////////////////////////////////
// End of If Statement Processing Logic //
//////////////////////////////////////////
export function doesCopyContainRouterLink(copy) {
return copy.includes(this.dynamicStrings.ROUTER_LINK);
}
@ -229,14 +408,14 @@ export function getRouterLinkRouteFromCopy(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];
return copy.split(':')[1].split(',')[0];
}
export function getRouterLinkDisplayTextFromCopy(copy) {
// sample input: {routerLink: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];
return copy.split(':')[1].split(',')[1];
}
// Copy returned from the CMS that has newlines will return blocks wrapped in
@ -245,5 +424,5 @@ export function getRouterLinkDisplayTextFromCopy(copy) {
// attributes present
export function splitCMSCopyOnParagraphTag(copy) {
// filter removes empty strings that are a result of string.split with regex
return copy.split(/(?:<p(?:.*?)>)|(?:<\/p>)/g).filter((paragraph) => paragraph !== "");
return copy.split(/(?:<p(?:.*?)>)|(?:<\/p>)/g).filter((paragraph) => paragraph !== '');
}

View file

@ -30,13 +30,13 @@ export default({
isDismissible: false,
messageCopy: "",
messageHeadline: "",
type: "",
type: ""
},
headerAnswers: null
};
},
props: {
cmsWidgetName: String,
cmsWidgetName: String
},
computed: {
imageSrc()
@ -51,7 +51,7 @@ export default({
return {
"background-color": this.headerAnswers ? this.headerAnswers.HexCode : "#FFFFFF"
};
},
}
},
mounted() {
const answers = this.getCmsContent(this.cmsWidgetName, "Answers");

View file

@ -0,0 +1,361 @@
<template>
<buttonQuestion ref="buttonQuestion"
:answers="servicePackageAnswers"
:groupName="groupName"
buttonTypeString="servicePackageRadio"
:buttonTypeObject="servicePackageRadio"
v-model="selectedPackageName"
:validationRules="validationRules"
:isRequired="isRequired" />
</template>
<script>
import buttonQuestion from '@/digital-components/button-question/button-question';
import baseMixin from '@/mixins/base-mixin.js';
import { processIfStatements } from '@/helpers/cms-content-helper';
import { damageLocationsSelected as glassLocations } from '@/constants/damage-locations-selected';
import servicePackageRadio from './service-package-radio/service-package-radio';
import { partTypeStrings } from '@/constants/part-type-strings';
const packageNames = {
TIER_ONE: 'TierOne',
TIER_TWO: 'TierTwo',
TIER_THREE: 'TierThree'
};
export default {
name: 'servicePackageQuestion',
props: {
cmsWidgetName: String,
groupName: String,
validationRules: String,
isRequired: Boolean,
availableLineItems: []
},
data() {
return {
servicePackageRadio: servicePackageRadio,
selectedPackageName: null
};
},
watch: {
availableLineItems() {
if (this.allGlassPartsAndSupportingItemsHavePrices(this.$store.lineItems)) {
this.selectDefaultPackage();
}
},
selectedPackageName(newValue) {
const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue);
this.$emit('vapsItemsSelected', VapsProductsInSelectedPackage);
}
},
computed: {
nullSafeAvailableLineItems() {
return this.availableLineItems ?? [];
},
servicePackageAnswers() {
if (!this.cmsWidgetName) return {};
const cmsAnswersContent = [
{
Name: 'EconomyServicePackage',
cmsWidgetName: 'EconomyServicePackage'
},
{
Name: 'StandardServicePackage',
cmsWidgetName: 'StandardServicePackage'
},
{
Name: 'PremiumServicePackage',
cmsWidgetName: 'PremiumServicePackage'
}
];
//This isn't mainly about header text, header text is just the canary in the coal mine
//Vue will often hit this code twice during a pageload. The first time without
//having yet loaded cms content- and so this just skips that first time
if (this.getCmsContent(cmsAnswersContent[0].cmsWidgetName, 'HeaderText') == '') {
return {};
}
console.log(this.getCmsContent(this.cmsWidgetName, 'HeaderText'));
const modifiedAnswers = [{
Answers: {
value: answer.Name,
buttonLabel: this.getHeaderTextFromCms(this.cmsWidgetName),
buttonLabelSubCopy: this.getSubheaderTextFromCms(this.cmsWidgetName),
buttonBodyCopy: this.getBodyTextFromCms(this.cmsWidgetName),
buttonAuxiliaryCopy: 'Aux Copy', //his.getPackagePriceString(answer.Name),
buttonFooterCopy: this.getFooterTextFromCms(this.cmsWidgetName),
}
}];
return modifiedAnswers;
},
frontWipersApplicableForTierTwo() {
const frontWipersAreAvailable = this.lineItemsContainsPartType(
partTypeStrings.FRONT_WIPER
);
const isRepair = this.$store.order.damage.isRepair;
const glassToReplaceContainsWindshield = this.glassToReplaceContainsGlassLocation(
glassLocations.WINDSHIELD
);
if (frontWipersAreAvailable) {
if (isRepair) {
return true;
} else {
if (glassToReplaceContainsWindshield) {
return true;
} else {
return false;
}
}
} else {
return false;
}
},
rearWiperApplicableForTierTwo() {
const rearWiperIsAvailable = this.lineItemsContainsPartType(partTypeStrings.REAR_WIPER);
return (
this.glassToReplaceContainsGlassLocation(glassLocations.REAR) &&
rearWiperIsAvailable
);
},
frontWipersApplicableForTierThree() {
const frontWipersAreAvailable = this.lineItemsContainsPartType(
partTypeStrings.FRONT_WIPER
);
return frontWipersAreAvailable;
},
rearWiperApplicableForTierThree() {
const rearWiperIsAvailable = this.lineItemsContainsPartType(partTypeStrings.REAR_WIPER);
const frontWipersAreAvailable = this.lineItemsContainsPartType(
partTypeStrings.FRONT_WIPER
);
return (
rearWiperIsAvailable &&
(this.glassToReplaceContainsGlassLocation(glassLocations.REAR) ||
!frontWipersAreAvailable)
);
},
rainDefenseApplicableForTierThree() {
if (
this.rearWiperApplicableForTierTwo &&
!this.frontWipersApplicableForTierTwo &&
this.frontWipersApplicableForTierThree
) {
return false;
} else {
return true;
}
},
shouldDisplayTierTwoPackage() {
return this.frontWipersApplicableForTierTwo || this.rearWiperApplicableForTierTwo;
}
},
methods: {
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 'As little as $' + formattedPriceFloat;
},
getPackagePrice(packageName) {
let priceFloat = this.isInsuranceSelected
? 0
: baseMixin.methods.getTierOnePackagePrice(
baseMixin.methods.filterOutFees(this.nullSafeAvailableLineItems)
);
if (packageName === packageNames.TIER_TWO) {
priceFloat += this.getTierTwoPackageVapsPrice();
} else if (packageName === packageNames.TIER_THREE) {
priceFloat += this.getTierThreePackageVapsPrice();
}
return priceFloat;
},
getTierTwoPackageVapsPrice() {
let vapsPrice = 0;
const priceFrontWipers = this.frontWipersApplicableForTierTwo;
const priceRearWipers = this.rearWiperApplicableForTierTwo;
this.nullSafeAvailableLineItems.forEach((item) => {
if (
(priceFrontWipers &&
item.partType.toUpperCase() === partTypeStrings.FRONT_WIPER) ||
(priceRearWipers && item.partType.toUpperCase() === partTypeStrings.REAR_WIPER)
) {
vapsPrice += baseMixin.methods.getTotalLineItemPrice(item);
}
});
return vapsPrice;
},
getTierThreePackageVapsPrice() {
let vapsPrice = 0;
const priceFrontWipers = this.frontWipersApplicableForTierThree;
const priceRearWipers = this.rearWiperApplicableForTierThree;
const priceRainDefense = this.rainDefenseApplicableForTierThree;
this.nullSafeAvailableLineItems.forEach((item) => {
if (
(priceFrontWipers &&
item.partType.toUpperCase() === partTypeStrings.FRONT_WIPER) ||
(priceRearWipers &&
item.partType.toUpperCase() === partTypeStrings.REAR_WIPER) ||
(priceRainDefense &&
item.partType.toUpperCase() === partTypeStrings.RAIN_DEFENSE)
) {
vapsPrice += baseMixin.methods.getTotalLineItemPrice(item);
}
});
return vapsPrice;
},
selectDefaultPackage() {
const vapsFromStore = this.$store.lineItems.vaps;
let lowestTierForPackage = packageNames.TIER_ONE;
if (vapsFromStore?.length > 0) {
vapsFromStore.every((vapsItem) => {
let lowestTierForThisItem = this.getLowestTierForThisItem(vapsItem);
if (lowestTierForThisItem === packageNames.TIER_THREE) {
lowestTierForPackage = packageNames.TIER_THREE;
return false;
} else if (lowestTierForThisItem === packageNames.TIER_TWO) {
lowestTierForPackage = packageNames.TIER_TWO;
return true;
} else {
return true;
}
});
}
this.selectedPackageName = lowestTierForPackage;
},
allGlassPartsAndSupportingItemsHavePrices(lineItems) {
if (lineItems?.glassParts) {
for (let i = 0; i < lineItems.glassParts.length; i++) {
if (this.priceIsNullOrZero(lineItems.glassParts[i])) {
return false;
}
}
}
if (lineItems?.supportingItems) {
for (let i = 0; i < lineItems.supportingItems.length; i++) {
if (this.priceIsNullOrZero(lineItems.supportingItems[i])) {
return false;
}
}
}
return true;
},
priceIsNullOrZero(lineItem) {
return (
(lineItem.kitPrice == null || lineItem.kitPrice == 0) &&
(lineItem.laborAmount == null || lineItem.laborAmount == 0) &&
(lineItem.sellingPrice == null || lineItem.sellingPrice == 0)
);
},
getLowestTierForThisItem(vapsItem) {
let lowestTierForThisItem = null;
switch (vapsItem.partType) {
case partTypeStrings.FRONT_WIPER:
if (this.frontWipersApplicableForTierThree) {
lowestTierForThisItem = packageNames.TIER_THREE;
}
if (this.frontWipersApplicableForTierTwo) {
lowestTierForThisItem = packageNames.TIER_TWO;
}
break;
case partTypeStrings.REAR_WIPER:
if (this.rearWiperApplicableForTierThree) {
lowestTierForThisItem = packageNames.TIER_THREE;
}
if (this.rearWiperApplicableForTierTwo) {
lowestTierForThisItem = packageNames.TIER_TWO;
}
break;
case partTypeStrings.RAIN_DEFENSE:
if (this.rainDefenseApplicableForTierThree) {
lowestTierForThisItem = packageNames.TIER_THREE;
}
break;
}
return lowestTierForThisItem;
},
getVapsLineItemsForSelectedPackage(packageName) {
const vapsLineItemsForSelectedPackage = [];
if (packageName === packageNames.TIER_TWO) {
if (this.frontWipersApplicableForTierTwo) {
vapsLineItemsForSelectedPackage.push(
...this.getLineItemsContainingPartType(partTypeStrings.FRONT_WIPER)
);
}
if (this.rearWiperApplicableForTierTwo) {
vapsLineItemsForSelectedPackage.push(
...this.getLineItemsContainingPartType(partTypeStrings.REAR_WIPER)
);
}
} else if (packageName === packageNames.TIER_THREE) {
if (this.frontWipersApplicableForTierThree) {
vapsLineItemsForSelectedPackage.push(
...this.getLineItemsContainingPartType(partTypeStrings.FRONT_WIPER)
);
}
if (this.rearWiperApplicableForTierThree) {
vapsLineItemsForSelectedPackage.push(
...this.getLineItemsContainingPartType(partTypeStrings.REAR_WIPER)
);
}
if (this.rainDefenseApplicableForTierThree) {
vapsLineItemsForSelectedPackage.push(
...this.getLineItemsContainingPartType(partTypeStrings.RAIN_DEFENSE)
);
}
}
return vapsLineItemsForSelectedPackage;
},
getCustomValueFromString(str) {
switch (str) {
case 'isRecalibrationOnOrder':
return this.isRecalibrationOnOrder;
case 'frontWipersApplicableForTierTwo':
return this.frontWipersApplicableForTierTwo;
case 'rearWiperApplicableForTierTwo':
return this.rearWiperApplicableForTierTwo;
case 'frontWipersApplicableForTierThree':
return this.frontWipersApplicableForTierThree;
case 'rearWiperApplicableForTierThree':
return this.rearWiperApplicableForTierThree;
case 'rainDefenseApplicableForTierThree':
return this.rainDefenseApplicableForTierThree;
default:
return null;
}
},
getLineItemsContainingPartType(partType) {
const partTypeMatches = this.nullSafeAvailableLineItems.filter(
(lineItem) => lineItem.partType.toUpperCase() === partType
);
return partTypeMatches;
},
lineItemsContainsPartType(partType) {
const partTypeMatches = this.getLineItemsContainingPartType(partType);
return !!partTypeMatches.length;
},
glassToReplaceContainsGlassLocation(glassLocation) {
const glassLocationMatches =
this.$store.order.damage.glassToReplace?.filter(
(glassToReplace) => glassToReplace.glassLocation === glassLocation
) ?? [];
return !!glassLocationMatches.length;
}
},
components: {
buttonQuestion
}
};
</script>

View file

@ -0,0 +1,267 @@
<template>
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
<div class="package-label"
:class="[this.buttonLabelSubCopy ? 'has-subheader' : '']"
for="testradio">
<div class="package-specs" style="max-height:1000px;">
<p class="m-0">
<span v-html="this.buttonLabel"></span>
<span class="pricing-info" v-html="this.buttonAuxiliaryCopy"></span>
</p>
<p class="sub-label m-0"
v-if="this.buttonLabelSubCopy"
v-html="this.buttonLabelSubCopy"></p>
<div class="hide-when-closed" style="display: block;">
<!-- 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="text"
:text="getRouterLinkDisplayTextFromCopy(copy)"
href="#!"
@click-event="
$emit('buttonEvent', {
eventName: 'openModal',
args: getRouterLinkRouteFromCopy(copy),
})
"
:data-bs-target="'#' + getRouterLinkRouteFromCopy(copy)"
aria-label="Modal window" />
</span>
</template>
</li>
</ul>
<!-- End of body text parsing -->
<div class="package-footer fw-bold caption ms-n6"
v-if="this.buttonFooterCopy"
v-html="this.buttonFooterCopy"></div>
</div>
</div>
</div>
</baseInputButton>
</template>
<script>
import baseInputButton from '@/digital-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 {
getRouterLinkDisplayTextFromCopy,
getRouterLinkRouteFromCopy,
splitCopyOnCMSPlaceHolder,
doesCopyContainTextLink,
} from '@/helpers/cms-content-helper';
export default {
name: 'servicePackageRadio',
mixins: [inputButtonWrapperMixin],
components: {
baseInputButton,
textLink
},
computed: {
arrayOfListItemsFromBodyText() {
return this.getArrayOfListItemsFromRawCmsCopy(this.buttonBodyCopy);
}
},
methods: {
getRouterLinkDisplayTextFromCopy,
getRouterLinkRouteFromCopy,
splitCopyOnCMSPlaceHolder,
doesCopyContainTextLink,
stripUlTagFromCopy(copy) {
if (copy) {
const regex = /(?:<ul(?:.*?)>)|(?:<\/ul>)/g;
return copy.replace(regex, '');
}
},
getArrayOfListItemsFromRawCmsCopy(copy) {
if (copy) {
const withoutUlTags = this.stripUlTagFromCopy(copy);
return withoutUlTags
.split(/(?:<li(?:.*?)>)|(?:<\/li>)/g)
.filter((lineItem) => lineItem);
}
}
}
};
</script>
<style lang="scss" scoped>
.package-main {
.package-wrapper {
margin: 0.5rem 0;
label {
display: block;
}
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: 0.5rem;
overflow: hidden;
min-height: 60px;
&.has-subheader {
min-height: 80px;
}
&: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 {
.package-specs {
.hide-when-closed {
display: block;
}
}
}
+ .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;
}
}
}
}
.package-footer {
color: $red;
}
.package-specs {
display: flex;
flex-direction: column;
width: 100%;
max-height: 0;
transition: all 0.5s ease;
p {
font-weight: 500;
display: flex;
justify-content: space-between;
span {
&.pricing-info {
color: $green;
font-size: 0.875rem;
}
}
&.sub-label {
color: $green;
text-transform: uppercase;
font-size: 0.75rem;
}
}
ul {
margin: 1rem 0 0 -15px;
padding: 0;
li {
margin-bottom: 0.5rem;
font-size: 0.875rem;
line-height: 1.714;
a {
line-height: 1.714;
padding: 0;
}
}
}
@keyframes slideaway {
from {
display: block;
}
to {
transform: translateY(40px);
opacity: 0;
}
}
.hide-when-closed {
display: none;
}
}
}
}
</style>

View file

@ -0,0 +1,151 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<div class="fade-on-route-transition sub-container make-tall">
<servicePackageQuestion ref="servicePackage"
cmsWidgetName="ServicePackage"
groupName="ServicePackageQuestion"
:availableLineItems="availableLineItems"
@vapsItemsSelected="vapsItemsSelectedAction"
v-on="{ 'buttonEvent.openModal': openModalAction }"
validationRules="option-required"
isRequired />
<textBlock cmsWidgetName="PriceDisclaimerWidget"
justifyText="left"
typeStyle="caption"
class="mt-4" />
<!--<contentGroupModal ref="RainDefenseModal" cmsWidgetName="RainDefenseModal" />
<contentGroupModal ref="FrontWiperModal" cmsWidgetName="FrontWiperModal" />
<contentGroupModal ref="RearWiperModal" cmsWidgetName="RearWiperModal" />
<contentGroupModal ref="RecalModal" cmsWidgetName="RecalModal" />-->
<siteFooter cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isBackButtonHidden="shouldHideBackButton"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</Form>
</template>
<script>
// Components
import siteHeader from '@/iss-components/site-header/site-header';
import siteFooter from '@/iss-components/site-footer/site-footer';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import servicePackageQuestion from './service-package-question/service-package-question';
import textBlock from '@/digital-components/text-block/text-block';
//import contentGroupModal from '@/iss-components/content-group-modal/content-group-modal';
import loadingModal from '@/iss-components/loading-modal/loading-modal.vue';
//import vehicleQuestionsMixin from '../../mixins/vehicle-questions-mixin';
import { settleAllPromises } from '@/helpers/layout-helper';
import { useMainStore } from "@/store";
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { required } from '@/helpers/validation-rules';
import { errorMessages } from '@/constants/error-messages';
import { Form, defineRule } from 'vee-validate';
//import { navigateToHeritageFunnel } from '@/helpers/heritage-integration/navigation-helper';
//import { applicationConfig } from '@/constants/application-config';
defineRule('option-required', required(errorMessages.OPTION_REQUIRED));
export default {
name: 'service-packages',
async beforeRouteEnter(to, from, next) {
const store = useMainStore();
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
const carId = store.order.vehicle.carId;
const serviceZipCode = store.order.serviceLocation.zipCode;
const wipersPromise = store.getWipers(carId, serviceZipCode);
const rainDefensePromise = store.getRainDefense();
const supportingItemsPromise = store.getSupportingItems();
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise
},
{
resultKey: 'wipers',
promise: wipersPromise
},
{
resultKey: 'rainDefense',
promise: rainDefensePromise
},
{
resultKey: 'supportingItems',
promise: supportingItemsPromise
}
];
const resultMap = await settleAllPromises(promiseResultMap);
const clonedGlassParts = store.order.lineItems.glassParts
? JSON.parse(JSON.stringify(store.order.lineItems.glassParts))
: [];
const availableLineItems = [
resultMap.rainDefense,
...resultMap.supportingItems,
...resultMap.wipers,
...clonedGlassParts
];
const pricingResults = await baseMixin.methods.dispatchStoreAction(
storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
{
availableLineItems: availableLineItems
},
false
);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.pricedGlassParts = clonedGlassParts;
vm.supportingItems = resultMap.supportingItems;
vm.availableLineItems = pricingResults;
});
},
data() {
return {
selectedVaps: null,
availableLineItems: null,
supportingItems: null,
pricedGlassParts: null
};
},
methods: {
openModalAction(modalName) {
this.$refs[modalName].openModal();
},
arePagePrerequisitesValid() {
return (
store.getters.order.serviceLocation.zipCode &&
store.getters.order.serviceLocation.zipCodeCtu &&
(store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0)) &&
store.getters.order.referralNumber?.length !== 6
);
},
vapsItemsSelectedAction(vapsItemsSelected) {
this.selectedVaps = vapsItemsSelected;
},
backButtonAction() {
this.$router.navigate(
this.navigationScenarios.CLICKED_BACK,
this.$route
);
}
},
components: {
siteHeader,
siteFooter,
siteSubHeader,
Form,
textBlock,
servicePackageQuestion,
loadingModal
}
};
</script>

View file

@ -22,6 +22,12 @@ const vueApp = createApp(App);
*/
vueApp.config.unwrapInjectedRef = true;
vueApp.config.compilerOptions.isCustomElement = (tag) => {
return (tag === 'siteSubHeader' ||
tag === 'ServicePackages' ||
tag === 'servicePackageQuestion');
}
// Pinia
const pinia = createPinia();
vueApp.use(pinia);

View file

@ -10,7 +10,7 @@ export default {
buttonLabel: [Number, String],
buttonLabelSubCopy: String,
buttonBodyCopy: String,
buttonAuxillaryCopy: String,
buttonAuxiliaryCopy: String,
buttonFooterCopy: String,
buttonImage: String,
buttonImageId: String,

View file

@ -54,6 +54,7 @@ const routes = [
router.addRoute({
path: routeData[0].path, // Always the same path, because we control it with query strings.
name: routeData[0].name,
query: to.query,
component: routeData[0].component,
});
@ -68,15 +69,16 @@ const routes = [
console.log(error);
GoToStartOn404(next);
}
return null;
}
}];
const router = createRouter({
history: createWebHistory("/"),
routes,
routes
});
router.afterEach((to, from) => {
router.afterEach((to, from) => { /*eslint-disable-line*/
const store = useMainStore();
// Update lastPageVisited in the store
@ -106,12 +108,12 @@ async function GetRouteInfoFromPageName(pageName) {
routeData.push({
path: "/",
name: `${key}`,
component: lazyLoadComponent(jsonFromResponse[key].LayoutName),
component: lazyLoadComponent(jsonFromResponse[key].LayoutName)
});
});
return routeData;
};
}
//Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example.
router.overrideNavigation = (
@ -141,7 +143,7 @@ router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams =
}
// Navigate to the next route, depending on the scenario.
function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) { /*eslint-disable-line*/
if (!scenario) {
console.error("No scenario provided. Please review the routing table.");
return;
@ -170,7 +172,7 @@ function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams =
router.push({
name: "root",
query: Object.assign(optionalQuery, {
issPage: matchingScenarioMap.destinationIssPageValue,
issPage: matchingScenarioMap.destinationIssPageValue
}),
params: optionalParams
});
@ -182,7 +184,7 @@ function navigate (scenario, currentRoute, optionalQuery = {}, optionalParams =
// Navigate to an external url.
function navigateToUrl(url, optionalQuery = {}) {
// possibly show some loading screen in the future here.
let externalUrl = new URL(url);
const externalUrl = new URL(url);
for (const queryKey in optionalQuery) {
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
@ -219,7 +221,7 @@ function GoToStartOn404(next) {
router.addRoute({
path: "/",
name: errorPageName,
component: lazyLoadComponent(errorPageName),
component: lazyLoadComponent(errorPageName)
});
// Put item on the bus
@ -230,13 +232,13 @@ function GoToStartOn404(next) {
isDismissible: true,
messageCopy: "You can get a quote by starting on this page.",
messageHeadline: "We're sorry, something went wrong.",
type: globalEventTypes.Danger,
type: globalEventTypes.Danger
}
);
next({
name: errorPageName,
query: {issPage: errorPageName },
query: {issPage: errorPageName }
});
};
@ -249,14 +251,14 @@ async function runExperiments(nextPage) {
await store.runExperimentsForTrigger({
userId: getDeviceIdValue(),
triggerEvent: experimentTriggers.SITE_ENTRY,
triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE,
triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE
});
}
await store.runExperimentsForTrigger({
userId: getDeviceIdValue(),
triggerEvent: experimentTriggers.PAGE_ENTRY,
triggerValue: nextPage,
triggerValue: nextPage
});
}

View file

@ -1,28 +1,27 @@
export const issPageValues = {
ENTRY_PAGE: "entry-page",
WELCOME_PAGE: "welcome-page",
POLICY_HOLDER_DETAILS: "policy-holder-details",
VEHICLE_MAKE: "vehicle-make",
VEHICLE_YEAR: "vehicle-year",
VEHICLE_MODEL: "vehicle-model",
VEHICLE_STYLE: "vehicle-style",
VEHICLE_DAMAGE: "vehicle-damage",
VEHICLE_LOOKUP: "vehicle-lookup",
ADDRESS_LOOKUP: "address-lookup",
ADDRESS_VEHICLES: "address-vehicles",
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
VIN_LOOKUP: "vin-lookup",
VEHICLE_PARTS: "vehicle-parts",
PART_QUESTIONS: "part-questions",
MOLDING_QUESTIONS: "molding-questions",
CAPABILITY_QUESTIONS: "capability-questions",
COVERAGE_STATEMENT: "coverage-statement",
PROVIDER_PREFERENCE: "provider-preference",
SERVICE_LOCATION: "service-location",
REVEAL: "reveal",
ESTIMATE: "estimate",
ADDRESS_VEHICLES: "address-vehicles",
QUOTE: "quote",
HERITAGE: "heritage",
ENTRY_PAGE: 'entry-page',
WELCOME_PAGE: 'welcome-page',
POLICY_HOLDER_DETAILS: 'policy-holder-details',
VEHICLE_MAKE: 'vehicle-make',
VEHICLE_YEAR: 'vehicle-year',
VEHICLE_MODEL: 'vehicle-model',
VEHICLE_STYLE: 'vehicle-style',
VEHICLE_DAMAGE: 'vehicle-damage',
VEHICLE_LOOKUP: 'vehicle-lookup',
ADDRESS_LOOKUP: 'address-lookup',
ADDRESS_VEHICLES: 'address-vehicles',
LICENSE_PLATE_LOOKUP: 'license-plate-lookup',
VIN_LOOKUP: 'vin-lookup',
VEHICLE_PARTS: 'vehicle-parts',
PART_QUESTIONS: 'part-questions',
MOLDING_QUESTIONS: 'molding-questions',
CAPABILITY_QUESTIONS: 'capability-questions',
COVERAGE_STATEMENT: 'coverage-statement',
PROVIDER_PREFERENCE: 'provider-preference',
SERVICE_LOCATION: 'service-location',
REVEAL: 'reveal',
ESTIMATE: 'estimate',
ADDRESS_VEHICLES: 'address-vehicles',
SERVICE_PACKAGE: 'service-package',
};

View file

@ -118,6 +118,7 @@ export const useMainStore = defineStore({
vehicle: (state) => state.order.vehicle,
damage: (state) => state.order.damage,
lineItems: (state) => state.order.lineItems,
hasAnyNonWindshieldGlassParts: (state) => !state.order.policy.isDamageGlassOnly,
eventBusItem: (state) => ( eventCategory, eventSubCategory) => {
const matchedEvent = state.applicationUser.eventBus.find(
@ -433,7 +434,58 @@ export const useMainStore = defineStore({
capabilityAnswerResults: capabilityQuestionAnswersForPart,
},
});
},
},
getWipers() {
const carId = this.order.vehicle.carId;
///WARNING WARNING DANGER WILL ROBINSON
///TODO: this is temp test code until serviceLocation is complete.
//const serviceZipCode = this.order.serviceLocation.zipCode;
const serviceZipCode = '44902';
return globalMethods
.callHttpClient({
method: endpoints.GetWipers.method,
endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}`
})
.catch((error) => {
// The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow
console.error(error);
return [];
});
},
getRainDefense() {
return globalMethods
.callHttpClient({
method: endpoints.GetRainDefense.method,
endpoint: `${endpoints.GetRainDefense.url}`
})
.catch((error) => {
// The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow
console.error(error);
return [];
});
},
getSupportingItems() {
const glassPartsArray = this.order.lineItems.glassParts ?? [];
const carId = this.order.vehicle.carId;
const isRepair = this.order.damage.isRepair;
const numberOfChips = this.order.damage.numberOfChips;
return globalMethods
.callHttpClient({
method: endpoints.GetSupportingItems.method,
endpoint: endpoints.GetSupportingItems.url,
payload: {
carId: carId,
serviceType: isRepair ? 'Repair' : 'Replace',
parentAccountNumber: 0,
parts: glassPartsArray,
numberOfRepairChips: isRepair ? numberOfChips : 0
}
});
},
lookupVehicleByVin(vin) {
return globalMethods.callHttpClient({
method: endpoints.LookupVehicleByVin.method,