From 85cad5a6ce041657a6340ac4240aff58ca076275 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Tue, 7 Oct 2025 14:57:12 -0400 Subject: [PATCH 1/5] CASH-1637 CASH-1637 correcting fields iteratively since dev environment is unstable --- src/mixins/analytics-mixin.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 74b74283b..50115bbf9 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -221,7 +221,7 @@ export default { ? order?.serviceLocation?.provider?.address?.zipCodeCtu : order?.serviceLocation?.zipCodeCtu; label.subTotalPrice = getSubTotal(order?.lineItems); - label.totalPrice = getAmountDue(order?.lineItems); + label.totalPrice = getAmountDue(order?.lineItems, true); await this.pushEventToGA( GaCategories.FMG_SESSION_DATA, @@ -237,9 +237,14 @@ export default { (lineItem) => lineItem.partType == partTypeStrings.EARLY_BIRD ); + const promoCodes = order?.lineItems?.promos + ?.map((item) => item.promoCode) + .filter((code) => code) + .join(", "); + label = {}; label.serviceType = order?.serviceLocation?.appointmentType; - label.promoCodes = order?.lineItems?.promos; + label.promoCodes = promoCodes; label.hasTechnicianNotes = order?.serviceLocation?.techNotes ? true : false; label.paymentMethod = order?.payment?.piaType; label.isTextingOptedIn = order?.customer?.isSmsOptIn ? true : false; From 58e2ec0addacd163c7467c8516e6b9e8abec8e44 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Tue, 7 Oct 2025 15:06:41 -0400 Subject: [PATCH 2/5] CASH-1637 CASH-1637 check for array to prevent exception when not --- src/mixins/analytics-mixin.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 50115bbf9..3ff326e1c 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -237,10 +237,12 @@ export default { (lineItem) => lineItem.partType == partTypeStrings.EARLY_BIRD ); - const promoCodes = order?.lineItems?.promos - ?.map((item) => item.promoCode) - .filter((code) => code) - .join(", "); + const promoCodes = Array.isArray(order?.lineItems?.promos) + ? order?.lineItems?.promos + ?.map((item) => item.promoCode) + .filter((code) => code) + .join(", ") + : ""; label = {}; label.serviceType = order?.serviceLocation?.appointmentType; From 7f28449d1f479c98a078ecceaed2a196ea40d9c8 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Tue, 7 Oct 2025 15:10:18 -0400 Subject: [PATCH 3/5] CASH-1637 CASH-1637 optional chaining ? not necessary since checking for an array already --- src/mixins/analytics-mixin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 3ff326e1c..f5a7d10e0 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -239,7 +239,7 @@ export default { const promoCodes = Array.isArray(order?.lineItems?.promos) ? order?.lineItems?.promos - ?.map((item) => item.promoCode) + .map((item) => item.promoCode) .filter((code) => code) .join(", ") : ""; From a6df3a6ff6b95070e583d878227d40d54e25ec22 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Wed, 15 Oct 2025 13:23:22 -0400 Subject: [PATCH 4/5] CASH-1637 CASH-1637 call fmg session data logging endpoint --- src/constants/analytics.js | 3 - src/constants/endpoints.js | 4 ++ src/constants/store-actions.js | 1 + src/mixins/analytics-mixin.js | 117 ++++++++++++++++++------------- src/router/methods/after-each.js | 6 +- src/store/index.js | 101 ++++++++++++++++++++++++++ 6 files changed, 178 insertions(+), 54 deletions(-) diff --git a/src/constants/analytics.js b/src/constants/analytics.js index 77bb78a3e..fe43be06d 100644 --- a/src/constants/analytics.js +++ b/src/constants/analytics.js @@ -7,8 +7,6 @@ const analyticsPageEvents = { const GaEvents = { GENERIC_EVENT: "event", PAGE_VIEW_EVENT: "logPageview", - FMG_DATA_EVENT_1: "logEvent1", - FMG_DATA_EVENT_2: "logEvent2", }; const GaCategories = { @@ -16,7 +14,6 @@ const GaCategories = { EVOX: "Evox", APPOINTMENT: "Appointment", SERVICE_LOCATION: "service-location", - FMG_SESSION_DATA: "fmg_session_data", }; const GaActions = { diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index eb55495f5..17a8f9a08 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -180,6 +180,10 @@ const endpoints = { url: "/analytics/api/v1/analytics/digitalconsumer-log", method: "POST", }, + LogFmgSessionData: { + url: "/analytics/api/v1/analytics/digitalconsumer-session-logging", + method: "POST", + }, GetExperimentsByUser: { url: "/analytics/api/v1/analytics/get-experiments", method: "GET", diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 209266131..513891ea1 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -64,6 +64,7 @@ const storeActions = { INITIALIZE_SESSION: "initializeSession", LOG_PART_QUESTIONS: "logPartQuestions", LOG_DIGITALCONSUMER: "logDigitalConsumer", + LOG_FMG_SESSION_DATA: "logFmgSessionData", // DEPENDENCY MUTATIONS RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleStateAndDependencies", diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index f5a7d10e0..fbaccb113 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -200,39 +200,22 @@ export default { await this.logPageView(analyticsPageEvents.ENTRY); }, - async pushFmgDataToGA() { + // 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() { const hasSubmittedOrder = baseMixin.methods.hasSubmittedOrder(); const submittedOrder = baseMixin.methods.getSubmittedOrder(); const order = hasSubmittedOrder ? submittedOrder : store.getters.order; - //event 1 - var label = {}; - label.carId = order?.vehicle?.carId; - label.hasVin = order?.vehicle?.vin ? true : false; - label.cashOrInsuranceAccountType = order?.payment?.isInsurance ? "Insurance" : "Cash"; - label.damageType = order?.damage?.isRepair ? "Repair" : "Replace"; - label.productType = order?.damage?.glassToReplace; - label.recalRequired = containsRecalParts(order?.lineItems); - label.recalType = getRecalPartNumbers(order?.lineItems?.glassParts); - label.serviceZipCode = order?.serviceLocation?.provider?.address?.zipCode - ? order?.serviceLocation?.provider?.address?.zipCode - : order?.serviceLocation?.zipCode; - label.providerCtu = order?.serviceLocation?.provider?.address?.zipCodeCtu - ? order?.serviceLocation?.provider?.address?.zipCodeCtu - : order?.serviceLocation?.zipCodeCtu; - label.subTotalPrice = getSubTotal(order?.lineItems); - label.totalPrice = getAmountDue(order?.lineItems, true); + const hasSubmittedApplicationUser = baseMixin.methods.hasSubmittedApplicationUser(); + const submittedApplicationUser = baseMixin.methods.getSubmittedApplicationUser(); + const applicationUser = hasSubmittedApplicationUser + ? submittedApplicationUser + : store.getters.applicationUser; - await this.pushEventToGA( - GaCategories.FMG_SESSION_DATA, - GaEvents.FMG_DATA_EVENT_1, - JSON.stringify(label), - true, - null, - null - ); - - //event 2 const isEarlyBird = order?.lineItems?.supportingItems?.find( (lineItem) => lineItem.partType == partTypeStrings.EARLY_BIRD ); @@ -242,28 +225,66 @@ export default { .map((item) => item.promoCode) .filter((code) => code) .join(", ") - : ""; + : null; - label = {}; - label.serviceType = order?.serviceLocation?.appointmentType; - label.promoCodes = promoCodes; - label.hasTechnicianNotes = order?.serviceLocation?.techNotes ? true : false; - label.paymentMethod = order?.payment?.piaType; - label.isTextingOptedIn = order?.customer?.isSmsOptIn ? true : false; - label.isEarlyBird = isEarlyBird ? true : false; - label.insuranceCo = order?.policy?.insuranceCompanyName; - label.deductible = order?.policy?.currentDeductible; - label.isVerified = order?.payment?.insuranceCoverage?.isVerified; - label.isNoComp = order?.policy?.isNoComp; - label.isItac = order?.policy?.isItac; + const glassProducts = order?.damage?.glassToReplace?.map( + (part) => `${part.glassLocation}-${part.glassName}` + ); - await this.pushEventToGA( - GaCategories.FMG_SESSION_DATA, - GaEvents.FMG_DATA_EVENT_2, - JSON.stringify(label), - true, - null, - null + var sessionData = {}; + sessionData.currentPage = getPageNameFromRouter(); + sessionData.sid = getSessionIdValue(); + sessionData.deviceId = getDeviceIdValue(); + sessionData.fmgSessionId = applicationUser?.savedSessionId; + 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 = glassProducts; + 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.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 = `${order?.schedule?.date} ${order?.schedule?.startTime}`; + 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.isNoComp = order?.policy?.isNoComp; + sessionData.isItac = order?.policy?.isItac; + sessionData.subTotalPrice = getSubTotal(order?.lineItems); + sessionData.totalPrice = getAmountDue(order?.lineItems, true); + + await baseMixin.methods.dispatchStoreAction( + storeActions.LOG_FMG_SESSION_DATA, + sessionData, + false ); }, diff --git a/src/router/methods/after-each.js b/src/router/methods/after-each.js index 7be0e5ce0..1e1f80134 100644 --- a/src/router/methods/after-each.js +++ b/src/router/methods/after-each.js @@ -4,6 +4,9 @@ export async function afterEach(to, from) { // digital consumer logging analyticsMixin.methods.logDigitalConsumer(); + // digital consumer fmg session logging to snowflake + analyticsMixin.methods.pushFmgSessionData(); + // Push page view to GA analyticsMixin.methods.pushPageViewToGA(); @@ -12,7 +15,4 @@ export async function afterEach(to, from) { // Push current order status to Data Layer analyticsMixin.methods.pushOrderToDataLayer(); - - // Push session data GA events for Analytics - analyticsMixin.methods.pushFmgDataToGA(); } diff --git a/src/store/index.js b/src/store/index.js index 1fa9b4f14..cf2326d53 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1594,6 +1594,107 @@ export const actions = { }); }, + logFmgSessionData( + context, + { + currentPage, + sid, + deviceId, + fmgSessionId, + userId, + carId, + vehicleYear, + vehicleMake, + vehicleModel, + vehicleStyle, + hasVin, + cashOrInsuranceAccountType, + currentDeductible, + damageType, + productType, + eon, + referralNumber, + referralSequenceNumber, + referralDate, + workOrderNumber, + workOrderId, + isPia, + piaType, + parentAccountNumber, + settledTenderAmount, + recalRequired, + recalType, + serviceZipCode, + providerCtu, + appointmentDate, + serviceType, + promoCodes, + hasTechnicianNotes, + paymentMethod, + isTextingOptedIn, + isEarlyBird, + insuranceCo, + deductible, + isVerified, + isNoComp, + isItac, + subTotalPrice, + totalPrice, + } + ) { + var payload = { + currentPage: currentPage, + sid: sid, + deviceId: deviceId, + fmgSessionId: fmgSessionId, + userId: userId, + carId: carId, + vehicleYear: vehicleYear, + vehicleMake: vehicleMake, + vehicleModel: vehicleModel, + vehicleStyle: vehicleStyle, + hasVin: hasVin, + cashOrInsuranceAccountType: cashOrInsuranceAccountType, + isVerified: isVerified, + currentDeductible: currentDeductible, + 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, + }; + + return globalMethods.callHttpClient({ + method: endpoints.LogFmgSessionData.method, + endpoint: endpoints.LogFmgSessionData.url, + payload: payload, + }); + }, + // Misc Actions setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) { context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); From 5bf467dfffe388af0c5399a2c212aefcb04919d2 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Wed, 15 Oct 2025 13:37:57 -0400 Subject: [PATCH 5/5] CASH-1637 CASH-1637 dupe deductible field --- src/store/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index cf2326d53..991391e38 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1609,7 +1609,6 @@ export const actions = { vehicleStyle, hasVin, cashOrInsuranceAccountType, - currentDeductible, damageType, productType, eon, @@ -1656,7 +1655,6 @@ export const actions = { hasVin: hasVin, cashOrInsuranceAccountType: cashOrInsuranceAccountType, isVerified: isVerified, - currentDeductible: currentDeductible, damageType: damageType, productType: productType, eon: eon,