CASH-2399 Handle error scenario where Adyen fails to load. Will navigate back to payment-method on error.
978 lines
40 KiB
JavaScript
978 lines
40 KiB
JavaScript
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 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.subTotalPrice = getSubTotal(order?.lineItems);
|
||
sessionData.totalPrice = getAmountDue(order?.lineItems, true);
|
||
sessionData.userAgent = navigator.userAgent;
|
||
sessionData.cashPriceSubTotal = order?.cashPriceSubTotal;
|
||
|
||
await baseMixin.methods.dispatchStoreAction(
|
||
storeActions.LOG_FMG_SESSION_DATA,
|
||
sessionData,
|
||
false
|
||
);
|
||
},
|
||
|
||
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 = "";
|
||
}
|
||
|
||
// 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 = "";
|
||
}
|
||
|
||
// 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 don’t display the price)
|
||
if (
|
||
order.payment.isInsurance &&
|
||
isDefined(order.payment.insuranceCoverage.isVerified) &&
|
||
!order.payment.insuranceCoverage.isVerified
|
||
) {
|
||
payload.priceSubTotal = "";
|
||
}
|
||
//deductible (in scenarios we don’t 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 don’t display the price)
|
||
if (
|
||
order.payment.isInsurance &&
|
||
isDefined(order.payment.insuranceCoverage.isVerified) &&
|
||
!order.payment.insuranceCoverage.isVerified
|
||
) {
|
||
payload.priceTotal = "";
|
||
}
|
||
//deductible (in scenarios we don’t 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
|
||
);
|
||
|
||
if (productSku == "DISCOUNT" || productType == "SERVICE PACKAGE DISCOUNT") {
|
||
if (coupon) {
|
||
coupon += ",";
|
||
}
|
||
if (productSku == "DISCOUNT") {
|
||
coupon += promoCode;
|
||
} else {
|
||
coupon += productSku;
|
||
}
|
||
|
||
discount += productPrice * -1;
|
||
} 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 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);
|
||
}
|
||
|
||
//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;
|
||
}
|