DigitalConsumer.ISS/src/store/index.js
2023-01-10 09:07:35 -05:00

768 lines
26 KiB
JavaScript

import { defineStore } from "pinia";
import { endpoints } from "@/constants/endpoints.js";
import { getDateForSavedSessionTimeout } from "@/helpers/session-helper";
import globalMethods from "@/global-methods";
import { experimentTriggers } from "@/constants/experiments";
import { applicationConfig } from "@/constants/application-config";
import { issPageValues } from "@/router/router-constants/issPage-values";
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
const storeId = 'main';
const getDefaultState = () => {
return {
order: {
vehicle: {
year: null,
make: null,
model: null,
style: null,
carId: null,
category: null,
vin: null,
imageUrl: null,
imageVifNumber: null,
imageColor: null,
registration: {
licensePlate: null,
address: null,
city: null,
state: null,
zipCode: null,
firstName: null,
lastName: null,
},
},
damage: {
isRepair: null,
numberOfChips: null,
glassToReplace: null,
partQuestionAnswers: null,
moldingQuestionAnswers: null,
capabilityQuestionAnswers: null,
},
customer: {
emailAddress: null,
},
serviceLocation: {
address: null,
city: null,
state: null,
zipCode: null,
},
lineItems: {
glassParts: null,
otherParts: null
},
payment: {
isInsurance: true,
insuranceCoverage: {
isVerified: false,
},
},
referralNumber: null,
referralDate: null,
accountNumber: 0,
},
applicationUser: {
lastPageVisited: null,
experiments: [],
triggeredSiteEntry: false,
eventBus: [],
pageData: {},
savedSessionTimeout: getDateForSavedSessionTimeout(),
saveSessionPromise: null,
savedSessionId: null,
crmCustomerId: null,
lastPageVisited: null,
triggeredSiteEntry: false,
},
issConfig: {
clientName: "",
styleSheet: "",
clientTag: "",
}
};
};
export const state = getDefaultState();
export const useMainStore = defineStore({
id: storeId,
state: () => state,
getters: {
vehicle: (state) => state.order.vehicle,
damage: (state) => state.order.damage,
lineItems: (state) => state.order.lineItems,
eventBusItem: (state) => ( eventCategory, eventSubCategory) => {
const matchedEvent = state.applicationUser.eventBus.find(
( { category, subCategory } ) =>
category === eventCategory && subCategory === eventSubCategory
);
return matchedEvent?.eventValue;
},
eventBus: (state) => state.applicationUser.eventBus,
applicationUserObj: (state) => state.applicationUser,
pageData: (state) => (page) => {
return state.applicationUser.pageData[page];
},
experimentOrder: (state) => {
return {
issVehicleYear: state.order.vehicle.year,
issVehicleMake: state.order.vehicle.make,
issVehicleModel: state.order.vehicle.model,
issVehicleStyle: state.order.vehicle.style,
issIsRepair: state.order.damage.isRepair,
issNumberOfChips: state.order.damage.numberOfChips,
issCarId: state.order.vehicle.carId,
issServiceCity: state.order.serviceLocation.city,
issServiceState: state.order.serviceLocation.state,
issServiceZipCode: state.order.serviceLocation.zipCode,
issParentAccountNumber: state.order.accountNumber,
issIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
issHasRecalibrationPart: getHasRecalibrationPart(state),
issSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
issSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.WINDSHIELD),
issSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.REAR),
issSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.DRIVER),
issSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.damage.glassToReplace,
"glassLocation"
).includes(damageLocationsSelected.PASSENGER),
issOrderPartNumbers: [
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"partNumber"
),
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.otherParts,
"partNumber"
),
],
issOrderPartTypes: [
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
),
...getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.otherParts,
"recalibrationType"
),
],
};
},
experimentSettings: (state) =>
state.applicationUser.experiments
.map((x) => x.settings)
.reduce((r, c) => Object.assign(r, c), {}) ?? {}
},
actions:
{
// Content API Actions
getRouteInfo(pageName) {
return globalMethods.callHttpClient({
method: endpoints.GetRouteInfo.method,
endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION),
payload: {
pageName: pageName,
},
});
},
getHomepageName() {
return globalMethods.callHttpClient({
method: endpoints.GetHomepageInfo.method,
endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION),
});
},
getPageData(pageName) {
return globalMethods.callHttpClient({
method: endpoints.GetPageData.method,
endpoint: endpoints.GetPageData.url(applicationConfig.APPLICATION_ABBREVIATION, pageName),
payload: {},
});
},
// Vehicle API Actions
getVehicleYears() {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleYears.method,
endpoint: endpoints.GetVehicleYears.url,
payload: {},
});
},
getVehicleMakes() {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleMakes.method,
endpoint: endpoints.GetVehicleMakes.url + this.order.vehicle.year,
payload: {},
});
},
getVehicleModels() {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleModels.method,
endpoint: `${endpoints.GetVehicleModels.url}/${this.order.vehicle.year}/${this.order.vehicle.make}`,
payload: {},
});
},
getVehicleStyles() {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleStyles.method,
endpoint: `${endpoints.GetVehicleStyles.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}`,
payload: {},
});
},
getDamageOptions(carId) {
return globalMethods.callHttpClient({
methods: endpoints.GetDamageOptions.method,
endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
payload: {},
});
},
async getParts() {
const vehicle = this.getters.vehicle;
const damage = this.getters.damage;
const order = this.order;
const carId = vehicle.carId;
const glassArray = damage.glassToReplace;
const resultsArray = damage.partQuestionAnswers;
const zipCode = order.serviceLocation.zipCode;
const vin = vehicle.vin;
// create a new array to avoid mutating state
const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);
const resultsArrayForPayload = convertResultsForApi(resultsArray);
const response = await globalMethods.callHttpClient({
method: endpoints.GetParts.method,
endpoint: endpoints.GetParts.url,
payload: {
carId: carId,
glassPieces: glassArrayForPayload,
answerResults: resultsArrayForPayload,
zip: zipCode,
vin: vin,
},
});
// Flatten location and name properties
response.data.glassPieceParts = convertGlassPieceNamingFromApi(
response.data.glassPieceParts
);
return response;
},
getCapabilityQuestions(carId, partNumber) {
return globalMethods.callHttpClient({
method: endpoints.GetCapabilityQuestions.method,
endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`,
});
},
setVehicle() {
return globalMethods
.callHttpClient({
methods: endpoints.GetVehicle.method,
endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`,
payload: {},
})
.then((response) => {
this.updateVehicle(response.data);
return response;
});
},
saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount) {
const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort();
const isGlassToReplaceTheSame =
this.order.damage.glassToReplace?.length === selectedGlassToReplace.length &&
this.order.damage.glassToReplace
.slice()
.sort()
.every(
(obj, index) =>
obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation &&
obj.glassName === selectedGlassPassedInSorted[index].glassName
);
const isWindshieldRepairTheSame =
isWindshieldRepair === this.order.damage.isRepair;
const isChipCountTheSame =
selectedWindshieldChipCount === this.order.damage.numberOfChips;
const isDamageChanging =
!isGlassToReplaceTheSame ||
!isWindshieldRepairTheSame ||
(isWindshieldRepair && !isChipCountTheSame);
if (isDamageChanging) {
//Reset dependent state when changing
this.resetGlassPartsState();
// Save new values
this.updateIsRepair(isWindshieldRepair);
this.updateNumberOfChips(isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null);
this.updateGlassToReplace(selectedGlassToReplace);
}
},
updateVehicle(vehicle) {
this.order.vehicle.carId = vehicle.carId;
this.order.vehicle.category = vehicle.category;
this.order.vehicle.imageUrl = vehicle.imageUrl;
this.order.vehicle.imageVifNumber = vehicle.imageVifNumber;
this.order.vehicle.imageColor = vehicle.imageColor;
},
resetVehicleState()
{
this.order.vehicle.year = null;
this.order.vehicle.make = null;
this.order.vehicle.model = null;
this.order.vehicle.style = null;
this.order.vehicle.carId = null;
this.order.vehicle.category = null;
this.order.vehicle.vin = null;
this.order.vehicle.imageUrl = null;
this.order.vehicle.imageVifNumber = null;
this.order.vehicle.imageColor = null;
},
resetGlassPartsState() {
this.order.lineItems.glassParts = null;
this.order.damage.partQuestionAnswers = null;
this.order.damage.moldingQuestionAnswers = null;
this.order.damage.capabilityQuestionAnswers = null;
this.applicationUser.pageData[issPageValues.PART_QUESTIONS] = null;
this.applicationUser.pageData[issPageValues.VEHICLE_PARTS] = null;
this.applicationUser.pageData[issPageValues.MOLDING_QUESTIONS] = null;
this.applicationUser.pageData[issPageValues.CAPABILITY_QUESTIONS] = null;
},
resetSupportingItemsState() {
this.order.lineItems.supportingItems = null;
},
resetDamageState() {
this.order.damage.isRepair = null;
this.order.damage.numberOfChips = null;
this.order.damage.glassToReplace = null;
},
resetMoldingAndCapabilityQuestionAnswersIfNeeded(matchedParts) {
const partsOrQuestionsDataToCompareWith =
this.pageData(issPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ??
this.pageData(issPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ??
[];
const previouslySelectedPartNumbers = getAllPartNumbers(partsOrQuestionsDataToCompareWith);
const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts);
const haveSelectedVehiclePartsChanged =
previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
if (haveSelectedVehiclePartsChanged) {
this.updateGlassParts(null);
this.resetSupportingItemsState();
this.updateMoldingQuestionAnswers(null);
this.updateCapabilityQuestionAnswers(null);
this.updatePageData({ page: issPageValues.MOLDING_QUESTIONS, data: null});
this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null});
}
},
updateVehicleYear(year) {
if(this.order.vehicle.year != year)
{
this.resetVehicleState();
this.resetDamageState();
this.order.vehicle.year = year;
}
},
updateVehicleMake(make) {
if(this.order.vehicle.make != make)
{
const year = this.order.vehicle.year;
this.resetVehicleState();
this.resetDamageState();
this.order.vehicle.year = year;
this.order.vehicle.make = make;
}
},
updateVehicleModel(model) {
if(this.order.vehicle.model != model)
{
const year = this.order.vehicle.year;
const make = this.order.vehicle.make;
this.resetVehicleState();
this.resetDamageState();
this.order.vehicle.year = year;
this.order.vehicle.make = make;
this.order.vehicle.model = model;
}
},
updateVehicleStyle(style) {
if(this.order.vehicle.style != style)
{
const year = this.order.vehicle.year;
const make = this.order.vehicle.make;
const model = this.order.vehicle.model;
this.resetVehicleState();
this.resetDamageState();
this.order.vehicle.year = year;
this.order.vehicle.make = make;
this.order.vehicle.model = model;
this.order.vehicle.style = style;
}
},
updateIsRepair(isRepair) {
this.order.damage.isRepair = isRepair;
},
updateNumberOfChips(numberOfChips) {
this.order.damage.numberOfChips = numberOfChips;
},
updateGlassToReplace(glassToReplace) {
this.order.damage.glassToReplace = glassToReplace;
},
updateGlassParts(partsData) {
this.order.lineItems.glassParts = partsData;
},
updateMoldingQuestionAnswers(answersArray) {
this.order.damage.moldingQuestionAnswers = answersArray;
},
updateCapabilityQuestionAnswers(answersArray) {
this.order.damage.capabilityQuestionAnswers = answersArray;
},
updatePartQuestionAnswers(answersArray) {
this.order.damage.partQuestionAnswers = answersArray;
},
updatePageData(pageData) {
this.applicationUser.pageData[pageData.page] = pageData.data;
},
savePartQuestionAnswers(partQuestionAnswersArray) {
// if part question answers have changed, reset subsequent question answers
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
context.getters.damage.partQuestionAnswers,
"result"
);
const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
partQuestionAnswersArray,
"result"
);
const havePartQuestionAnswersChanged =
sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length ||
!sortedPreviousResultsArray?.every(
(x, i) => x.result === sortedPartQuestionAnswersArray[i].result
);
if (havePartQuestionAnswersChanged) {
this.updateGlassParts(null);
this.updateMoldingQuestionAnswers(null);
this.updateCapabilityQuestionAnswers(null);
this.updatePageData({ page: issPageValues.VEHICLE_PARTS, data: null});
this.updatePageData({ page: issPageValues.MOLDING_QUESTIONS, data: null});
this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null});
}
//Save new values
this.updatePartQuestionAnswers(partQuestionAnswersArray);
},
addEventToBus (event) {
this.applicationUser.eventBus.push(event);
},
removeEventFromBus (eventData) {
const matchedEvent = this.applicationUser.eventBus.find(
( {category, subCategory } ) =>
category === eventData.category && subCategory === eventData.subCategory
);
const itemIndex = this.applicationUser.eventBus.indexOf(matchedEvent);
// If the item exists, remove it.
if (itemIndex > -1) {
this.applicationUser.eventBus.splice(itemIndex, 1);
}
},
// populate initial state
populateInitialState()
{
if(!localStorage.getItem(storeId)) {
this.$state = state;
}
},
// Analytics Actions
logExperimentExposure({ userId, sessionKey, pageName, experiment }) {
return globalMethods.callHttpClient({
method: endpoints.LogExperimentExposureIfAssigned.method,
endpoint: endpoints.LogExperimentExposureIfAssigned.url,
payload: {
experimentForLogging: {
userId: userId,
experimentUniverseId: experiment.universeId,
experimentUniverseName: experiment.universeName,
experimentTestId: experiment.testId,
experimentTestName: experiment.testName,
experimentVariationId: experiment.variationId,
experimentVariationName: experiment.variationName,
enabled: experiment.isActive,
isExposed: experiment.isExposed,
userPartitionNumber: experiment.userPartitionNumber,
assignmentId: experiment.assignmentId,
sessionKey: sessionKey,
pageName: pageName,
},
},
});
},
logPageView({ userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId, experimentsForUser })
{
var payload = {
userId: userId,
sessionKey: sessionKey,
sessionId: sessionId,
pageName: pageName,
applicationName: applicationConfig.APPLICATION_NAME,
action: action,
event: event,
shouldUseSessionId: shouldUseSessionId,
experimentsForUser: experimentsForUser,
}
return globalMethods.callHttpClient({
method: endpoints.LogPageView.method,
endpoint: endpoints.LogPageView.url,
payload: payload,
logApiCall: false,
});
},
logCustomEvent({ userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser})
{
if ( pageName == null || pageName.length == 0 )
pageName = "none";
var payload = {
userId: userId,
sessionKey: sessionKey,
sessionId: sessionId,
pageName: pageName,
applicationName: applicationConfig.APPLICATION_NAME,
category: category,
action: action,
label: label,
value: value,
shouldUseSessionId: shouldUseSessionId,
experimentsForUser: experimentsForUser,
};
return globalMethods.callHttpClient({
method: endpoints.LogCustomEvent.method,
endpoint: endpoints.LogCustomEvent.url,
payload: payload,
logApiCall: false,
});
},
initializeSession({ userId, sessionId, userAgent, referrer }) {
var payload = {
applicationName: applicationConfig.APPLICATION_NAME,
userId: userId,
deviceId: userId,
sessionId: sessionId,
userAgent: userAgent,
operatorId: "WEB",
userName: "SafeliteISS",
referrer: referrer,
};
return globalMethods.callHttpClient({
method: endpoints.InitializeSession.method,
endpoint: endpoints.InitializeSession.url,
payload: payload,
logApiCall: false,
});
},
updateLastPageVisited(lastPageVisited) {
this.applicationUser.lastPageVisited = lastPageVisited;
},
updateExperiments(experiments) {
this.applicationUser.experiments = experiments;
},
updateTriggeredSiteEntry(wasSiteEntryTriggered) {
this.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
},
GetExperimentsByUser(userId) {
return globalMethods.callHttpClient({
method: endpoints.GetExperimentsByUser.method,
endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`,
payload: {},
});
},
async runExperimentsForTrigger({ userId, triggerEvent, triggerValue }) {
if (triggerEvent == experimentTriggers.SITE_ENTRY) {
this.updateTriggeredSiteEntry(true);
}
var payload = {
applicationName: applicationConfig.APPLICATION_NAME,
userId: userId,
triggerEvent: triggerEvent,
triggerValue: triggerValue,
experimentOrder: this.experimentOrder,
};
const response = await globalMethods.callHttpClient({
method: endpoints.RunExperimentsForTrigger.method,
endpoint: endpoints.RunExperimentsForTrigger.url,
payload: payload,
});
this.updateExperiments(response.data.experiments);
},
},
persist: true
});
// Private Functions
function getHasRecalibrationPart(state) {
var hasRequiresRecalibration =
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"requiresRecalibration"
)?.length > 0;
var hasRecalibrationType =
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
)?.length > 0;
if (hasRequiresRecalibration) {
if (hasRecalibrationType) {
// Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType'
return (
getNonFalseValuesOfPropertyInArrayOfObjects(
state.order.lineItems.glassParts,
"recalibrationType"
)[0].toLowerCase() != "unknown"
);
} else {
// Has 'requiresRecalibration' but no 'recalibrationType' at all
return true;
}
} else {
// Does not have 'requiresRecalibration'
return false;
}
}
function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
return (array ?? []).map((x) => x[propertyName]).filter((x) => x);
}
function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
if (!arrayOfObjects) return null;
return arrayOfObjects.sort((a, b) => {
if (a[propertyName] < b[propertyName]) return -1;
else if (a[propertyName] > b[propertyName]) return 1;
else return 0;
});
}
function convertGlassPieceNamingForApi(glassArray) {
if (!glassArray || glassArray.length === 0) return [];
// check if array already converted. (likely when a session has been saved previously and then reloaded)
if (glassArray[0].location !== undefined) {
return glassArray;
}
const converted = [];
glassArray.forEach((glass) => {
converted.push({
location: glass.glassLocation,
name: glass.glassName,
});
});
return converted;
}
function convertResultsForApi(resultsArray) {
if (!resultsArray) return [];
const converted = [];
resultsArray.forEach((answer) => {
converted.push({
location: answer.glassLocation,
name: answer.glassName,
result: answer.result,
});
});
return converted;
}
function convertGlassPieceNamingFromApi(glassArray) {
glassArray.forEach((glass) => {
glass.glassLocation = glass.glassPiece.location;
glass.glassName = glass.glassPiece.name;
delete glass.glassPiece;
return glass;
});
return glassArray;
}
function getAllPartNumbers(partsOrQuestions) {
return partsOrQuestions[0]?.parts
? [...partsOrQuestions]
.map((glass) => glass.parts)
.flat()
.map((part) => part.partNumber)
.filter((partNumber) => !partNumber.toUpperCase().includes("FEE"))
.sort()
.join(",")
: [];
}