Initial digital session logging commits.

This commit is contained in:
Jeremy-Z 2026-03-16 12:20:21 -04:00
parent efee204fc8
commit 02b3e3c3f9
4 changed files with 346 additions and 0 deletions

View file

@ -181,6 +181,14 @@ const endpoints = Object.freeze({
url: `${ANALYTICS_BASE_URL}/initialize`,
method: 'POST'
},
LogDigitalConsumer: {
url: "/analytics/api/v1/analytics/digitalconsumer-log",
method: "POST",
},
LogIssSessionData: {
url: "/analytics/api/v1/analytics/digitalconsumer-session-logging",
method: "POST",
},
GetExperimentsByUser: {
url: `${ANALYTICS_BASE_URL}/get-experiments`,
method: 'GET'

View file

@ -161,6 +161,178 @@ export default {
return baseMethod.apply(object, arguments);
};
},
logDigitalConsumer() {
const store = useMainStore();
const currentPageName = this.getPageNameByQueryString();
const universes = store.applicationUser.experiments;
const deviceId = getDeviceIdValue(); // cookieNames.DXDEV
const userId = getUserIdValue(); // cookieNames.ISS_USER_ID
const sid = getSessionIdValue(); // cookieNames.SESSION_ID
const variationNames = universes
.filter((item) => item.universeName === experimentSettings.CONCEPT_FUNNEL)
.map((item) => item.variationName)
.filter(Boolean); // removes undefined/null
const conceptVariation = variationNames.length > 0 ? variationNames[0] : "";
const isConceptExposed = universes.find(
(item) => item.universeName === experimentSettings.CONCEPT_FUNNEL
)?.isExposed;
// TODO: Find equivalent ISS methods for these:
const submittedOrder = null;//baseMixin.methods.getSubmittedOrder();
const hasSubmittedOrder = false;//baseMixin.methods.hasSubmittedOrder();
const hasSubmittedOrderAtConfirmationPage =
hasSubmittedOrder && currentPageName?.toLowerCase() == routeData.CONFIRMATION.name;
var payload = {
deviceId: deviceId,
sessionId: sid,
actionName: `Browser page:${currentPageName}`,
referralSequenceNumber: hasSubmittedOrderAtConfirmationPage
? submittedOrder.referralSequenceNumber
: store.order.referralSequenceNumber,
referralNumber: hasSubmittedOrderAtConfirmationPage
? submittedOrder.referralNumber
: store.order.referralNumber,
workOrderId: hasSubmittedOrderAtConfirmationPage
? submittedOrder.workOrderId
: store.order.workOrderId,
workOrderNumber: hasSubmittedOrderAtConfirmationPage
? submittedOrder.workOrderNumber
: store.order.workOrderNumber,
conceptVariation: conceptVariation,
isConceptExposed: isConceptExposed,
};
store.logDigitalConsumer(payload);
},
// 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.
pushIssSessionData() {
const store = useMainStore();
const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder();
const submittedOrder = baseMixin.methods.getSubmittedOrder();
const order = hasSubmittedOrder ? submittedOrder : store.order;
const hasSubmittedApplicationUser = baseMixin.methods.hasSubmittedApplicationUser();
const submittedApplicationUser = baseMixin.methods.getSubmittedApplicationUser();
const applicationUser = hasSubmittedApplicationUser
? submittedApplicationUser
: store.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 currentPageName = this.getPageNameByQueryString();
/*
// add query strings to the page name for debugging. on the vehicle page, if from an external link, pull it from the stash
if (currentPageName === "vehicle") {
if (!window.location.search) {
if (store.getters.externalParameterState?.qsStash) {
currentPageName += `${store.getters.externalParameterState.qsStash}`;
}
} else {
currentPageName += `${window.location.search}`;
}
} else {
if (window.location.search) {
currentPageName += `${window.location.search}`;
}
}
*/
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;
store.logIssSessionData(sessionData);
},
async initSession() {
regenerateDeviceId();

View file

@ -178,6 +178,12 @@ router.afterEach(async (to, from) => {
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
document.title = routerTitles[to.query.issPage] || 'Safelite Solutions®';
// digital consumer logging
analyticsMixin.methods.logDigitalConsumer();
// digital consumer ISS session logging to snowflake
//analyticsMixin.methods.pushIssSessionData();
// Push page view to GA
analyticsMixin.methods.pushPageViewToGA();

View file

@ -2622,6 +2622,166 @@ export const useMainStore = defineStore({
updateTriggeredSiteEntry(wasSiteEntryTriggered) {
this.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
},
logDigitalConsumer(
{
deviceId,
sessionId,
actionName,
referralSequenceNumber,
referralNumber,
workOrderId,
workOrderNumber,
conceptVariation,
isConceptExposed,
}
) {
var payload = {
sessionId: sessionId,
deviceId: deviceId,
actionName: actionName ?? "",
referralSequenceNumber: referralSequenceNumber ?? "",
referralNumber: referralNumber ?? "",
applicationName: "ISS", //baseMixin.methods.isMobileDevice() ? "2.0 Mobile" : "2.0",
workOrderId: workOrderId ?? "",
workOrderNumber: workOrderNumber ?? "",
conceptVariation: conceptVariation,
isConceptExposed: isConceptExposed,
};
console.error("Logging Digital Consumer Action: ", payload);
return globalMethods.callHttpClient({
method: endpoints.LogDigitalConsumer.method,
endpoint: endpoints.LogDigitalConsumer.url,
payload,
logApiCall: false,
bailoutOnError: false
}).then(
(response) => response,
(error) => {
console.log(`Analytics Service Error: ${error.data}`);
}
);
},
logIssSessionData(
{
currentPage,
sid,
deviceId,
issSessionId,
skey,
userId,
carId,
vehicleYear,
vehicleMake,
vehicleModel,
vehicleStyle,
hasVin,
cashOrInsuranceAccountType,
damageType,
productType,
eon,
referralNumber,
referralSequenceNumber,
referralDate,
workOrderNumber,
workOrderId,
isPia,
piaType,
parentAccountNumber,
billToAccountNumber,
settledTenderAmount,
recalRequired,
recalType,
serviceZipCode,
providerCtu,
appointmentDate,
serviceType,
promoCodes,
hasTechnicianNotes,
paymentMethod,
isTextingOptedIn,
isEarlyBird,
insuranceCo,
deductible,
isVerified,
coverageStatus,
coverageSubStatus,
isNoComp,
isItac,
subTotalPrice,
totalPrice,
userAgent,
cashPriceSubTotal,
}
) {
var payload = {
currentPage: currentPage,
sid: sid,
deviceId: deviceId,
fmgSessionId: issSessionId,
skey: skey,
userId: userId,
carId: carId,
vehicleYear: vehicleYear,
vehicleMake: vehicleMake,
vehicleModel: vehicleModel,
vehicleStyle: vehicleStyle,
hasVin: hasVin,
cashOrInsuranceAccountType: cashOrInsuranceAccountType,
isVerified: isVerified,
coverageStatus: coverageStatus,
coverageSubStatus: coverageSubStatus,
damageType: damageType,
productType: productType,
eon: eon,
referralNumber: referralNumber,
referralSequenceNumber: referralSequenceNumber,
referralDate: referralDate,
workOrderNumber: workOrderNumber,
workOrderId: workOrderId,
isPia: isPia,
piaType: piaType,
parentAccountNumber: parentAccountNumber,
settledTenderAmount: settledTenderAmount,
recalRequired: recalRequired,
recalType: recalType,
serviceZipCode: serviceZipCode,
providerCtu: providerCtu,
appointmentDate: appointmentDate,
serviceType: serviceType,
promoCodes: promoCodes,
hasTechnicianNotes: hasTechnicianNotes,
paymentMethod: paymentMethod,
isTextingOptedIn: isTextingOptedIn,
isEarlyBird: isEarlyBird,
insuranceCo: insuranceCo,
deductible: deductible,
isNoComp: isNoComp,
isItac: isItac,
subTotalPrice: subTotalPrice,
totalPrice: totalPrice,
userAgent: userAgent,
cashPriceSubTotal: cashPriceSubTotal,
billToAccountNumber: billToAccountNumber,
};
return globalMethods.callHttpClient({
method: endpoints.LogIssSessionData.method,
endpoint: endpoints.LogIssSessionData.url,
payload,
logApiCall: false,
bailoutOnError: false
}).then(
(response) => response,
(error) => {
console.log(`Analytics Service Error: ${error.data}`);
}
);
},
updateContactInfo(contactInfo) {
this.order.customer.firstName = contactInfo?.firstName ?? this.order.customer.firstName;