DigitalConsumer.FixMyGlass/src/mixins/analytics-mixin.js
2026-06-26 11:44:25 -04:00

1036 lines
42 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { storeActions } from "@/constants/store-actions";
import {
getDeviceIdValue,
getSessionIdValue,
getSessionKeyValue,
getUserIdValue,
regenerateDeviceId,
regenerateUserId,
refreshSessionExpiration,
areAllSessionCookiesSet,
setSessionIdIfUnset,
setSessionKeyIfUnset,
getskeyValue,
} from "@/helpers/heritage-integration/cookie-helper";
import { queryStrings } from "@/constants/query-strings";
import { experimentSettings, experimentUniverses } from "@/constants/experiments";
import experimentMixin from "@/mixins/experiment-mixin";
import {
analyticsPageEvents,
GaCategories,
GaActions,
GaLabels,
GaEvents,
ValueToLogTypes,
} from "@/constants/analytics";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import store from "@/store";
import baseMixin from "@/mixins/base-mixin";
import { applicationConfig } from "../constants/application-config";
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
import { routeData } from "@/router/constants/routes";
import { getQuerystringParameter } from "@/helpers/querystring-helper";
import { containsRecalParts, getRecalPartNumbers } from "@/helpers/recal-helper";
import { getAmountDue, getSubTotal, getSalesTax } from "@/helpers/pricing-helper.js";
import { partTypeStrings } from "@/constants/part-type-strings";
import { bailoutCodes } from "@/constants/bailout-codes";
import router from "@/router";
export default {
methods: {
getPageName() {
return getPageNameFromRouter();
},
async logPageView(pageEvent) {
const currentPageName = getPageNameFromRouter();
await this.validateSession();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const hasSubmittedOrderAtConfirmationPage =
hasSubmittedOrder && currentPageName?.toLowerCase() == routeData.CONFIRMATION.name;
const refSequenceNum = hasSubmittedOrderAtConfirmationPage
? submittedOrder.referralSequenceNumber
: store.getters.order.referralSequenceNumber;
const parentAccountNum = hasSubmittedOrderAtConfirmationPage
? submittedOrder.payment.parentAccountNumber
: store.getters.order.payment.parentAccountNumber;
var payload = {
userId: getUserIdValue(),
sessionKey: getSessionKeyValue(),
pageName: currentPageName,
sessionId: getSessionIdValue(),
action: "",
event: pageEvent,
shouldUseSessionId: false,
experimentsForUser: store.getters.applicationUser.experiments,
referralSequenceNumber: refSequenceNum,
parentAccountNumber: parentAccountNum,
};
await baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false);
},
async logCustomEvent(category, action, label, value) {
const currentPageName = getPageNameFromRouter();
await this.validateSession();
const refSequenceNum =
store.getters.order.referralSequenceNumber ||
store.getters.submittedOrder?.referralSequenceNumber;
var payload = {
userId: getUserIdValue(),
sessionKey: getSessionKeyValue(),
pageName: currentPageName,
sessionId: getSessionIdValue(),
category: category,
action: action,
label: label,
value: value,
shouldUseSessionId: false,
experimentsForUser: store.getters.applicationUser.experiments,
referralSequenceNumber: refSequenceNum,
parentAccountNumber: store.getters.order.payment.parentAccountNumber,
};
await baseMixin.methods.dispatchStoreAction(
storeActions.LOG_CUSTOM_EVENT,
payload,
false
);
},
async pushEventToGA(
category,
action,
label,
pushToLogApp = false,
valueToLogType = null,
value = null
) {
const currentPageName = getPageNameFromRouter();
const labelToLog = getValueToLog(label, valueToLogType);
const eventToBePushed = {
event: GaEvents.GENERIC_EVENT,
category: category,
action: action,
label: labelToLog,
value: value ?? undefined,
path: `/fmg/${currentPageName}`,
};
pushToDataLayerIfDefined(eventToBePushed);
if (pushToLogApp) {
await this.logCustomEvent(category, action, labelToLog, undefined);
}
},
async pushEventForChatsToGA(category, action, label, pushToLogApp = false) {
const currentPageName = getPageNameFromRouter();
const value = `2.0_${currentPageName}`;
await this.pushEventToGA(category, action, label, pushToLogApp, null, value);
},
async pushVariableToDataLayer(data) {
pushToDataLayerIfDefined(data);
},
async pushPageViewToGA() {
const currentPageName = getPageNameFromRouter();
const pageViewEvent = {
event: GaEvents.PAGE_VIEW_EVENT,
pagePath: `/fmg/${currentPageName}`,
pageTitle: currentPageName,
};
pushToDataLayerIfDefined(pageViewEvent);
await this.logPageView(analyticsPageEvents.ENTRY);
},
// This sends session data to the session logging endpoint on the analytics service.
// From there, it uses aws kinesis data stream, DigitalConsumer-Session-Data-Stream,
// and aws FireHose stream, DigitalConsumer-Session-Firehose, to push data to an
// S3 bucket, safelite-dev-digitalconsumer-session-data-us-east-2/1.
// This bucket data is then picked up by snowflake for analytics use.
async pushFmgSessionData(errorJson = null) {
var currentPageName = getPageNameFromRouter(true);
if (!currentPageName) {
return;
}
if (errorJson) {
currentPageName += ` ERROR: ${errorJson}`;
}
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
const hasSubmittedApplicationUser = baseMixin.methods.hasSubmittedApplicationUser();
const submittedApplicationUser = baseMixin.methods.getSubmittedApplicationUser();
const applicationUser = hasSubmittedApplicationUser
? submittedApplicationUser
: store.getters.applicationUser;
const isEarlyBird = order?.lineItems?.supportingItems?.find(
(lineItem) => lineItem.partType == partTypeStrings.EARLY_BIRD
);
const promoCodes = Array.isArray(order?.lineItems?.promos)
? order?.lineItems?.promos
.map((item) => item.promoCode)
.filter((code) => code)
.join(", ")
: null;
const glassProducts = order?.damage?.glassToReplace?.map(
(part) => `${part.glassLocation}-${part.glassName}`
);
const format = (part) => `${part?.partNumber}-${part?.partType ? part.partType : ""}`;
const mapParts = (arr) => (arr ?? []).map(format);
const glassParts = mapParts(order?.lineItems?.glassParts);
const supportingItems = mapParts(order?.lineItems?.supportingItems);
const vaps = mapParts(order?.lineItems?.vaps);
const childParts = [].concat(
...(order?.lineItems?.glassParts ?? []).map((gp) => mapParts(gp?.childParts))
);
const allParts = [...glassParts, ...supportingItems, ...vaps, ...childParts];
if (applicationUser?.pageData?.quote?.servicePackageSelected) {
allParts.push(applicationUser.pageData.quote.servicePackageSelected);
}
var appointment = `${order?.schedule?.date ?? ""} ${order?.schedule?.startTime ?? ""}`;
var sessionData = {};
sessionData.currentPage = currentPageName;
sessionData.sid = getSessionIdValue();
sessionData.deviceId = getDeviceIdValue();
sessionData.fmgSessionId = applicationUser?.savedSessionId;
sessionData.skey = getskeyValue().toString();
sessionData.userId = getUserIdValue();
sessionData.carId = order?.vehicle?.carId;
sessionData.vehicleYear = order?.vehicle?.year;
sessionData.vehicleMake = order?.vehicle?.make;
sessionData.vehicleModel = order?.vehicle?.model;
sessionData.vehicleStyle = order?.vehicle?.style;
sessionData.hasVin = order?.vehicle?.vin ? true : false;
sessionData.cashOrInsuranceAccountType = order?.payment?.isInsurance
? "Insurance"
: "Cash";
sessionData.damageType = order?.damage?.isRepair ? "Repair" : "Replace";
sessionData.productType = allParts;
sessionData.eon = order?.eon;
sessionData.referralNumber = order?.referralNumber;
sessionData.referralSequenceNumber = order?.referralSequenceNumber;
sessionData.referralDate = order?.referralDate;
sessionData.workOrderNumber = order?.workOrderNumber;
sessionData.workOrderId = order?.workOrderId;
sessionData.isPia = order?.payment?.isPia ?? false;
sessionData.piaType = order?.payment?.piaType;
sessionData.parentAccountNumber = order?.payment?.parentAccountNumber;
sessionData.billToAccountNumber = order?.payment?.billToAccountNumber;
sessionData.settledTenderAmount = order?.settledTenderAmount;
sessionData.recalRequired = containsRecalParts(order?.lineItems);
sessionData.recalType = getRecalPartNumbers(order?.lineItems?.glassParts);
sessionData.serviceZipCode = order?.serviceLocation?.provider?.address?.zipCode
? order?.serviceLocation?.provider?.address?.zipCode
: order?.serviceLocation?.zipCode;
sessionData.providerCtu = order?.serviceLocation?.provider?.address?.zipCodeCtu
? order?.serviceLocation?.provider?.address?.zipCodeCtu
: order?.serviceLocation?.zipCodeCtu;
sessionData.appointmentDate = appointment;
sessionData.serviceType = order?.serviceLocation?.appointmentType;
sessionData.promoCodes = promoCodes;
sessionData.hasTechnicianNotes = order?.serviceLocation?.techNotes ? true : false;
sessionData.paymentMethod = order?.payment?.piaType;
sessionData.isTextingOptedIn = order?.customer?.isSmsOptIn ? true : false;
sessionData.isEarlyBird = isEarlyBird ? true : false;
sessionData.insuranceCo = order?.policy?.insuranceCompanyName;
sessionData.deductible = order?.policy?.currentDeductible;
sessionData.isVerified = order?.payment?.insuranceCoverage?.isVerified ?? false;
sessionData.coverageStatus = order?.payment?.insuranceCoverage?.coverageStatus;
sessionData.coverageSubStatus = order?.payment?.insuranceCoverage?.coverageSubStatus;
sessionData.isNoComp = order?.policy?.isNoComp;
sessionData.isItac = order?.policy?.isItac;
sessionData.isClaimAndCoverage = order.payment.isClaimAndCoverage;
sessionData.vinRequired = order.vehicle.vinRequired ?? false;
sessionData.installOemGlass = order.damage?.installOemGlass ?? false;
sessionData.subTotalPrice = getSubTotal(order?.lineItems);
sessionData.totalPrice = getAmountDue(order?.lineItems, true);
sessionData.userAgent = navigator.userAgent;
sessionData.cashPriceSubTotal = order?.cashPriceSubTotal;
sessionData.bailoutCode = this.getBailoutCodeEnum(
applicationUser?.pageData?.bailout?.bailoutCode
);
await baseMixin.methods.dispatchStoreAction(
storeActions.LOG_FMG_SESSION_DATA,
sessionData,
false
);
},
getBailoutCodeEnum(bailoutCode) {
if (bailoutCode) {
const bailoutCodeEnum = Object.keys(bailoutCodes).find(
(key) => bailoutCodes[key] === bailoutCode
);
return bailoutCodeEnum ? bailoutCode + "_" + bailoutCodeEnum : bailoutCode;
} else {
return null;
}
},
pushOrderToDataLayer() {
// helper check for if an object is defined (but maybe falsey)
const isDefined = (x) => x !== null && x !== undefined;
// Get correct order object
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const order = hasSubmittedOrder ? submittedOrder : store.getters.order;
// Begin assembling payload for data layer
const payload = {};
// Service Zip
if (
order.serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE &&
isDefined(order.serviceLocation.zipCode)
) {
payload.serviceZipCode = order.serviceLocation.zipCode;
} else if (
isDefined(order.serviceLocation.appointmentType) &&
order.serviceLocation.appointmentType !== AppointmentTypeStrings.MOBILE &&
isDefined(order.serviceLocation.provider.address.zipCode)
) {
payload.serviceZipCode = order.serviceLocation.provider.address.zipCode;
} else {
payload.serviceZipCode = "";
}
// Damage Type
if (isDefined(order.damage.isRepair)) {
payload.damageType = order.damage.isRepair ? "repair" : "replace";
} else {
payload.damageType = "";
}
// Account Type
if (isDefined(order.payment.isInsurance)) {
payload.accountType = order.payment.isInsurance ? "insurance" : "cash";
} else {
payload.accountType = "";
}
// Promo Codes
const promos = order.lineItems.promos ?? [];
if (promos.length === 0) {
payload.promoCodes = "";
} else {
const promoCodes = promos.map((promo) => promo.promoCode);
const promoString = promoCodes.reduce((prev, next) => `${prev},${next}`);
payload.promoCodes = promoString;
}
// Vehicle info
if (isDefined(order.vehicle.year)) {
// Ensure cast to string.
payload.vehicleYear = `${order.vehicle.year}`;
} else {
payload.vehicleYear = "";
}
if (isDefined(order.vehicle.make)) {
payload.vehicleMake = order.vehicle.make;
} else {
payload.vehicleMake = "";
}
if (isDefined(order.vehicle.model)) {
payload.vehicleModel = order.vehicle.model;
} else {
payload.vehicleModel = "";
}
if (isDefined(order.vehicle.style)) {
payload.vehicleStyle = order.vehicle.style;
} else {
payload.vehicleStyle = "";
}
if (isDefined(order.vehicle.carId)) {
payload.carId = order.vehicle.carId;
} else {
payload.carId = "";
}
if (isDefined(order.vehicle.vinRequired)) {
payload.vehicleIsVinRequired = order.vehicle.vinRequired;
} else {
payload.vehicleIsVinRequired = false;
}
// Glass pieces
const glass = order.damage.glassToReplace ?? [];
if (glass.length === 0) {
payload.glassToReplace = "";
} else {
const glassNames = glass.map((g) => `${g.glassLocation}/${g.glassName}`);
const glassString = glassNames.reduce((prev, next) => `${prev},${next}`);
payload.glassToReplace = glassString;
}
//EON
if (order.eon) {
payload.eon = order.eon;
} else {
payload.eon = "";
}
// Work Order Id
if (order.workOrderId) {
const parsedId = parseInt(order.workOrderId);
if (!isNaN(parsedId)) {
payload.workOrderId = parsedId;
} else {
payload.workOrderId = "";
}
} else {
payload.workOrderId = "";
}
// Provider Ctu
if (isDefined(order.serviceLocation.zipCodeCtu)) {
payload.providerCtu = order.serviceLocation.zipCodeCtu;
} else {
payload.providerCtu = "";
}
// Work Order Number
if (order.workOrderNumber) {
payload.orderNumber = order.workOrderNumber;
} else {
payload.orderNumber = "";
}
// Referral Sequence Number
if (order.referralSequenceNumber) {
payload.referralSequenceNumber = order.referralSequenceNumber;
} else {
payload.referralSequenceNumber = "";
}
// Pricing
// Only fire for completed orders?
const lineItems = order.lineItems ?? {};
const combinedLineItems = [
...(lineItems.glassParts ?? []),
...(lineItems.supportingItems ?? []),
...(lineItems.vaps ?? []),
...(lineItems.promos ?? []),
];
const isPricingAvailable =
combinedLineItems.length > 0 &&
combinedLineItems.every(
(lineItem) =>
isDefined(lineItem.kitPrice) &&
isDefined(lineItem.laborAmount) &&
isDefined(lineItem.sellingPrice)
);
const isTaxAvailable =
isPricingAvailable &&
combinedLineItems.every((lineItem) => isDefined(lineItem.salesTax));
//unverified (in scenarios we dont display the price)
if (
order.payment.isInsurance &&
isDefined(order.payment.insuranceCoverage.isVerified) &&
!order.payment.insuranceCoverage.isVerified
) {
payload.priceSubTotal = "";
}
//deductible (in scenarios we dont display the price)
else if (
order.payment.isInsurance &&
isDefined(order.payment.insuranceCoverage.isVerified) &&
order.payment.insuranceCoverage.isVerified &&
isDefined(order.policy.currentDeductible) &&
order.policy.currentDeductible >= 0 &&
!order.policy.isItac &&
!order.policy.isNoComp
) {
payload.priceSubTotal = "";
} else if (isPricingAvailable) {
const subtotal = baseMixin.methods
.getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, false)
.toFixed(2);
payload.priceSubTotal = parseFloat(subtotal);
} else {
payload.priceSubTotal = "";
}
// Cash Quote or Cash Price Sub Total
payload.cashPriceSubTotal = order?.cashPriceSubTotal ?? "";
//unverified (in scenarios we dont display the price)
if (
order.payment.isInsurance &&
isDefined(order.payment.insuranceCoverage.isVerified) &&
!order.payment.insuranceCoverage.isVerified
) {
payload.priceTotal = "";
}
//deductible (in scenarios we dont display the price)
else if (
order.payment.isInsurance &&
isDefined(order.payment.insuranceCoverage.isVerified) &&
order.payment.insuranceCoverage.isVerified &&
isDefined(order.policy.currentDeductible) &&
order.policy.currentDeductible >= 0 &&
!order.policy.isItac &&
!order.policy.isNoComp
) {
payload.priceTotal = "";
} else if (isTaxAvailable) {
const total = baseMixin.methods
.getTotalPriceOfAllLineItemsAndChildParts(combinedLineItems, true)
.toFixed(2);
payload.priceTotal = parseFloat(total);
} else {
payload.priceTotal = "";
}
// Recalibration
if (hasSubmittedOrder) {
payload.isRecalibrationOnOrder = store.getters.isRecalibrationOnSubmittedState;
} else {
payload.isRecalibrationOnOrder = store.getters.isRecalibrationOnOrder;
}
// Appointment Type
if (isDefined(order.serviceLocation.appointmentType)) {
payload.appointmentType = order.serviceLocation.appointmentType;
} else {
payload.appointmentType = "";
}
//Insurance
if (order.payment.isInsurance) {
payload.isInsuranceVerified = order.payment.insuranceCoverage.isVerified ?? "";
payload.insuranceCompanyName = order.policy.insuranceCompanyName ?? "";
if (!order.payment.insuranceCoverage.isVerified) {
payload.isInsuranceItac = "";
payload.isInsuranceNoComp = "";
} else {
payload.isInsuranceItac = order.policy.isItac ?? "";
payload.isInsuranceNoComp = order.policy.isNoComp ?? "";
}
if (
order.policy.isItac ||
order.policy.isNoComp ||
!order.payment.insuranceCoverage.isVerified
) {
payload.insuranceDeductible = "";
} else {
payload.insuranceDeductible = order.policy.currentDeductible ?? "";
}
} else {
payload.isInsuranceVerified = "";
payload.insuranceDeductible = "";
payload.isInsuranceItac = "";
payload.isInsuranceNoComp = "";
payload.insuranceCompanyName = "";
}
pushToDataLayerIfDefined(payload);
},
pushExperimentsToDataLayer() {
const experiments = store.getters.applicationUser.experiments;
experiments?.forEach((exp) => {
// Set Google Dimension Index based on experiment settings.
let googleDimensionIndex = 99;
if (exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX] !== undefined) {
googleDimensionIndex =
exp.settings[experimentSettings.GOOGLE_CUSTOM_DIMENSION_INDEX];
}
// Create object with dimension index and value.
const experimentWithDimension = {
[`experimentId_${googleDimensionIndex}`]: exp.universeId,
[`variationId_${googleDimensionIndex}`]: exp.variationId,
[`experimentName_${googleDimensionIndex}`]: exp.universeName,
[`variationName_${googleDimensionIndex}`]: exp.variationName,
[`customDimension_${googleDimensionIndex}`]: `${exp.universeId}_${exp.variationId}_${exp.universeName}_${exp.variationName}`,
};
// Push to the data layer with the Google Custom Dimension Index.
pushToDataLayerIfDefined(experimentWithDimension);
});
},
pushProductArrayToDataLayer() {
// Get correct order object
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
if (hasSubmittedOrder) {
var lineItems = submittedOrder.lineItems ? submittedOrder.lineItems : {};
// combine all line items
var combinedLineItems = [
...(lineItems.glassParts ?? []),
...(lineItems.supportingItems ?? []),
...(lineItems.vaps ?? []),
...(lineItems.promos ?? []),
];
//Get child parts
combinedLineItems = flattenArray(combinedLineItems);
var products = [];
var discount = 0;
var coupon = "";
var subTotal = 0;
for (var i = 0; i < combinedLineItems.length; i++) {
let part = combinedLineItems[i];
let productSku = part.partNumber;
let productType = part.partType;
let promoCode = part.promoCode;
let productPrice = baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts(
[part],
false
);
let isQuotePageDiscount = productType == partTypeStrings.QUOTE_PAGE_DISCOUNT;
if (
productSku == "DISCOUNT" ||
productType == "SERVICE PACKAGE DISCOUNT" ||
isQuotePageDiscount
) {
if (coupon) {
coupon += ",";
}
if (productSku == "DISCOUNT") {
coupon += promoCode;
} else {
coupon += productSku;
}
discount += productPrice * -1;
subTotal += productPrice;
} else {
products.push({
productType: productType,
productSku: productSku,
productPrice: productPrice.toFixed(2),
productQuantity: "1",
});
subTotal += productPrice;
}
}
pushToDataLayerIfDefined({
event: "productArray",
productArray: {
coupon: coupon,
discount: discount.toFixed(2),
subTotal: subTotal.toFixed(2),
products: products,
},
});
}
},
pushECommerceCartToDataLayer() {
const isDefined = (x) => x !== null && x !== undefined;
// Get correct order object
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
if (hasSubmittedOrder) {
var lineItems = submittedOrder.lineItems ? submittedOrder.lineItems : {};
var supportingItems =
submittedOrder.lineItems && submittedOrder.lineItems.supportingItems
? submittedOrder.lineItems.supportingItems
: [];
var insurance = submittedOrder.payment && submittedOrder.payment.isInsurance;
var itac = submittedOrder.policy && submittedOrder.policy.isItac;
var nocomp = submittedOrder.policy && submittedOrder.policy.isNoComp;
var recalLineItem = supportingItems.filter(function (lineItem) {
return lineItem.partType.indexOf("RECALIBRATION") !== -1;
});
var disposalFee = supportingItems.filter(function (lineItem) {
return lineItem.partType.indexOf("DISPOSAL FEE") !== -1;
});
var mobileFee = supportingItems.filter(function (lineItem) {
return lineItem.partType.indexOf("MOBILE FEE") !== -1;
});
var repairFee = supportingItems.filter(function (lineItem) {
return lineItem.partType.indexOf("REPAIR FEE") !== -1;
});
var replaceFee = supportingItems.filter(function (lineItem) {
return lineItem.partType.indexOf("REPLACE FEE") !== -1;
});
var quotePageDiscount = supportingItems.filter(function (lineItem) {
return lineItem.partType.indexOf(partTypeStrings.QUOTE_PAGE_DISCOUNT) !== -1;
});
var vaps =
submittedOrder.lineItems && submittedOrder.lineItems.vaps
? submittedOrder.lineItems.vaps
: [];
var wipers = vaps.filter(function (lineItem) {
return (
lineItem.partType.indexOf("FRONT WIPER") !== -1 ||
lineItem.partType.indexOf("REAR WIPER") !== -1
);
});
var rainRepel = vaps.filter(function (lineItem) {
return lineItem.partType.indexOf("RAIN REPEL") !== -1;
});
// Combine line items
var combinedLineItems = [];
if (lineItems.glassParts) {
combinedLineItems = combinedLineItems.concat(lineItems.glassParts);
}
if (recalLineItem) {
combinedLineItems = combinedLineItems.concat(recalLineItem);
}
if (disposalFee) {
combinedLineItems = combinedLineItems.concat(disposalFee);
}
if (mobileFee && !(insurance && !itac && !nocomp)) {
combinedLineItems = combinedLineItems.concat(mobileFee);
}
if (repairFee) {
combinedLineItems = combinedLineItems.concat(repairFee);
}
if (wipers) {
combinedLineItems = combinedLineItems.concat(wipers);
}
if (rainRepel) {
combinedLineItems = combinedLineItems.concat(rainRepel);
}
if (replaceFee) {
combinedLineItems = combinedLineItems.concat(replaceFee);
}
if (quotePageDiscount) {
combinedLineItems = combinedLineItems.concat(quotePageDiscount);
}
if (lineItems.promos) {
combinedLineItems = combinedLineItems.concat(lineItems.promos);
}
//Remove child Parts if any
combinedLineItems.forEach((lineItem) => {
lineItem.childParts = [];
});
var isPricingAvailable =
combinedLineItems.length > 0 &&
combinedLineItems.every(function (lineItem) {
return (
isDefined(lineItem.kitPrice) &&
isDefined(lineItem.laborAmount) &&
isDefined(lineItem.sellingPrice)
);
});
let subtotal = 0;
if (isPricingAvailable) {
subtotal = baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts(
combinedLineItems,
false
);
}
pushToDataLayerIfDefined({
event: "eCommerceCart",
eCommerceCart: {
products: combinedLineItems,
subTotal: parseInt(subtotal),
},
});
}
},
pushCommissionJunctionGtmDataToDataLayer() {
// helper check for if an object is defined (but maybe falsey)
const isDefined = (x) => x !== null && x !== undefined;
let repairReplace = "";
let coupons = "";
let refSequenceNum = "";
let accountType = "";
let amount = 0;
let cjEvent = "";
if (store.getters.applicationUser.affiliateCookies?.length > 0) {
const affiliateCookies = store.getters.applicationUser.affiliateCookies;
var cookieArray = [];
affiliateCookies.forEach((item) => {
let cookieObject = convertCookieStringToObject(item.CookieValue);
cookieArray.push(cookieObject);
});
const sortedCookies = cookieArray.sort(
(a, b) =>
new Date(b.timestamp.replace("/", "T")) -
new Date(a.timestamp.replace("/", "T"))
);
const filteredCookie = sortedCookies?.find(
(x) => x.tagEvent?.length > 0 && x.batchEvent?.length > 0
);
if (isDefined(filteredCookie)) {
cjEvent = filteredCookie.tagEvent;
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
if (hasSubmittedOrder) {
repairReplace = submittedOrder.damage.isRepair ? "Repair" : "Replace";
const promos = submittedOrder.lineItems.promos ?? [];
if (promos.length === 0) {
coupons = "";
} else {
const promoCodes = promos.map((promo) => promo.promoCode);
const promoString = promoCodes.reduce(
(prev, next) => `${prev}_${next}`
);
coupons = promoString;
}
const additionalDiscounts = submittedOrder.lineItems?.supportingItems?.find(
(x) => x.partType == partTypeStrings.QUOTE_PAGE_DISCOUNT
);
if (isDefined(additionalDiscounts)) {
if (coupons) {
coupons += ",";
}
coupons += additionalDiscounts.partNumber;
}
refSequenceNum = submittedOrder.referralSequenceNumber;
if (isDefined(submittedOrder.payment.isInsurance)) {
accountType = submittedOrder.payment.isInsurance ? "insurance" : "cash";
} else {
accountType = "";
}
const lineItems = submittedOrder.lineItems ?? {};
const combinedLineItems = [
...(lineItems.glassParts ?? []),
...(lineItems.supportingItems ?? []),
...(lineItems.vaps ?? []),
...(lineItems.promos ?? []),
];
const isPricingAvailable =
combinedLineItems.length > 0 &&
combinedLineItems.every(
(lineItem) =>
isDefined(lineItem.kitPrice) &&
isDefined(lineItem.laborAmount) &&
isDefined(lineItem.sellingPrice)
);
if (accountType == "cash" && isPricingAvailable) {
amount = baseMixin.methods.getTotalPriceOfAllLineItemsAndChildParts(
combinedLineItems,
false
);
}
}
}
pushToDataLayerIfDefined({
event: "commissionJunctionGtmData",
commissionJunctionGtmData: {
cj_commission_junction_event: cjEvent,
cj_referral_sequence_number: refSequenceNum,
cj_amount: amount.toFixed(2),
cj_repair_replace: repairReplace,
cj_coupon: coupons,
},
});
}
},
pushPageErrorToDataLayer(error) {
pushToDataLayerIfDefined({
event: "page-error",
error: error,
});
},
prependActionToMethod(object, method, actionToPrepend) {
const baseMethodName = method.name.startsWith("bound ")
? method.name.substring(6)
: method.name;
const baseMethod = object[baseMethodName];
object[baseMethodName] = function () {
actionToPrepend.apply(this, arguments);
return baseMethod.apply(object, arguments);
};
},
async initSession() {
regenerateDeviceId();
regenerateUserId();
const userId = getUserIdValue(); // cookieNames.FUNNEL_USER_ID
const deviceId = getDeviceIdValue(); // cookieNames.DXDEV
const sessionId = getSessionIdValue(); // cookieNames.SESSION_ID
const userAgent = navigator.userAgent; // navigator.userAgent
const referrer =
applicationConfig.CURRENT_ENVIRONMENT != "Localhost" ? document.referrer : null; // see above
const payload = {
userId: userId,
deviceId: deviceId,
sessionId: sessionId,
userAgent: userAgent,
referrer: referrer,
};
const response = await baseMixin.methods.dispatchStoreAction(
storeActions.INITIALIZE_SESSION,
payload,
false
);
if (response?.data) {
if (response.data.sessionKey) {
setSessionKeyIfUnset(response.data.sessionKey);
}
if (response.data.sessionId) {
setSessionIdIfUnset(response.data.sessionId);
}
}
// logging on session init so we capture complete query string data
await this.pushFmgSessionData();
},
noSession() {
return !areAllSessionCookiesSet();
},
// sessionExpired is true when one of the analytics cookies(sid, dxdev) has expired but we still have the vehicle year in vuex
sessionExpired() {
const fromHeritage = getQuerystringParameter(queryStrings.FROM_HERITAGE) === "true";
if (this.noSession() && !fromHeritage && store.getters.order.vehicle?.year > 0) {
return true;
} else {
return false;
}
},
async validateSession() {
// do not init session if it is expired. let the click event on nav-bar handle session expired logic
if (this.sessionExpired()) {
return;
}
if (this.noSession()) {
await this.initSession();
}
refreshSessionExpiration();
},
removeParamsFromEndpoint(endpoint) {
const endpointWithoutParams = endpoint.split("?")[0];
const numSlashesBeforeParams = 6;
let splitString = endpointWithoutParams.split("/");
if (splitString.length > numSlashesBeforeParams) {
splitString = splitString.slice(0, numSlashesBeforeParams);
return splitString.join("/");
} else {
return endpointWithoutParams;
}
},
},
computed: {
analyticsPageEvents() {
return analyticsPageEvents;
},
GaCategories() {
return GaCategories;
},
GaActions() {
return GaActions;
},
GaLabels() {
return GaLabels;
},
ValueToLogTypes() {
return ValueToLogTypes;
},
},
};
function pushToDataLayerIfDefined(data) {
if (window.dataLayer !== undefined) {
window.dataLayer.push(data);
}
}
function getPageNameFromRouter(useDefaultUrl = false) {
if (
router &&
router.currentRoute &&
router.currentRoute.value &&
router.currentRoute.value.name
) {
const query = router.currentRoute.value.query;
if (query && Object.keys(query).length > 0 && useDefaultUrl === true) {
const queryString = new URLSearchParams(query).toString();
return `${router.currentRoute.value.name}?${queryString}`;
}
return router.currentRoute.value.name;
}
return useDefaultUrl
? window.location.search
: window.location.href.replace(/\/$/, "").split("/").pop();
}
function getValueToLog(value, valueToLogType) {
if (valueToLogType != null && valueToLogType === ValueToLogTypes.LAST_5) {
return value.slice(-5);
}
return value;
}
function flattenArray(arr) {
let result = [];
arr.forEach((item) => {
result.push(item);
if (item.childParts) {
result = result.concat(item.childParts);
delete item.childParts; // Remove childParts after flattening
}
});
return result;
}
function convertCookieStringToObject(cookieValue) {
const cookieObject = cookieValue.split("&").reduce((acc, pair) => {
const [key, value] = pair.split("=");
acc[key] = value;
return acc;
}, {});
return cookieObject;
}