From b18f60cf03c735ded379b7fe34bd495102dee2b2 Mon Sep 17 00:00:00 2001 From: Eric Date: Sat, 24 Aug 2024 13:40:12 -0600 Subject: [PATCH] Website: Track reason behind psychological stage changes in start flow. (#21483) Changes: - Updated the `update-or-create-contact-and-account` helper to support a new input: `psychologicalStageChangedBy`, and to set the provided value on contact records. - Updated `save-questionnaire-progress` to set a psychologicalStageChangedBy value when updating contact records. --- .../api/controllers/save-questionnaire-progress.js | 9 +++++++++ .../update-or-create-contact-and-account.js | 9 ++++++++- website/api/hooks/custom/index.js | 11 +++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/website/api/controllers/save-questionnaire-progress.js b/website/api/controllers/save-questionnaire-progress.js index d2106c9c20aa..5cf77ba3cf81 100644 --- a/website/api/controllers/save-questionnaire-progress.js +++ b/website/api/controllers/save-questionnaire-progress.js @@ -203,6 +203,14 @@ module.exports = { } // Only update CRM records if the user's psychological stage changes. if(psychologicalStage !== userRecord.psychologicalStage) { + let psychologicalStageChangeReason = 'Website - Organic start flow'; // Default psystageChangeReason to "Website - Organic start flow" + if(this.req.session.adAttributionString && this.req.session.visitedSiteFromAdAt) { + let thirtyMinutesAgoAt = Date.now() - (1000 * 60 * 30); + // If this user visited the website from an ad, set the psychologicalStageChangeReason to be the adCampaignId stored in their session. + if(this.req.session.visitedSiteFromAdAt > thirtyMinutesAgoAt) { + psychologicalStageChangeReason = this.req.session.adAttributionString; + } + } // Update the psychologicalStageLastChangedAt timestamp if the user's psychological stage psychologicalStageLastChangedAt = Date.now(); sails.helpers.salesforce.updateOrCreateContactAndAccount.with({ @@ -212,6 +220,7 @@ module.exports = { primaryBuyingSituation: primaryBuyingSituation === 'eo-security' ? 'Endpoint operations - Security' : primaryBuyingSituation === 'eo-it' ? 'Endpoint operations - IT' : primaryBuyingSituation === 'mdm' ? 'Device management (MDM)' : primaryBuyingSituation === 'vm' ? 'Vulnerability management' : undefined, organization: this.req.me.organization, psychologicalStage, + psychologicalStageChangeReason, getStartedResponses: questionnaireProgressAsAFormattedString, contactSource: 'Website - Sign up', }).exec((err)=>{ diff --git a/website/api/helpers/salesforce/update-or-create-contact-and-account.js b/website/api/helpers/salesforce/update-or-create-contact-and-account.js index 208e48f40644..2ee68f09599b 100644 --- a/website/api/helpers/salesforce/update-or-create-contact-and-account.js +++ b/website/api/helpers/salesforce/update-or-create-contact-and-account.js @@ -30,6 +30,10 @@ module.exports = { '6 - Has team buy-in' ] }, + psychologicalStageChangeReason: { + type: 'string', + example: 'Website - Organic start flow' + }, contactSource: { type: 'string', isIn: [ @@ -56,7 +60,7 @@ module.exports = { }, - fn: async function ({emailAddress, linkedinUrl, firstName, lastName, organization, primaryBuyingSituation, psychologicalStage, contactSource, description, getStartedResponses}) { + fn: async function ({emailAddress, linkedinUrl, firstName, lastName, organization, primaryBuyingSituation, psychologicalStage, psychologicalStageChangeReason, contactSource, description, getStartedResponses}) { // Return undefined if we're not running in a production environment. if(sails.config.environment !== 'production') { sails.log.verbose('Skipping Salesforce integration...'); @@ -106,6 +110,9 @@ module.exports = { if(description) { valuesToSet.Description = description; } + if(psychologicalStageChangeReason) { + valuesToSet.Psystage_change_reason__c = psychologicalStageChangeReason;// eslint-disable-line camelcase + } let existingContactRecord; // Search for an existing Contact record using the provided email address or linkedIn profile URL. diff --git a/website/api/hooks/custom/index.js b/website/api/hooks/custom/index.js index 26e59b108971..d2bd549ddb96 100644 --- a/website/api/hooks/custom/index.js +++ b/website/api/hooks/custom/index.js @@ -147,6 +147,17 @@ will be disabled and/or hidden in the UI. res.locals.me = undefined; }//ļ¬ + // Check for query parameters set by ad clicks. + // This is used to track the reason behind a psychological stage change. + // If the user performs any action that causes a stage change + // within 30 minutes of visiting the website from an ad, their psychological + // stage change will be attributed to the ad campaign that brought them here. + if(req.param('utm_source') && req.param('creative_id') && req.param('campaign_id')){ + req.session.adAttributionString = `${req.param('utm_source')} ads - ${req.param('campaign_id')} - ${_.trim(req.param('creative_id'), '?')}`;// Trim questionmarks from the end of creative_id parameters. + // Example adAttributionString: Linkedin - 1245983829 - 41u3985237 + req.session.visitedSiteFromAdAt = Date.now(); + } + // Check for website personalizationĀ parameter, and if valid, absorb it in the session. // (This makes the experience simpler and less confusing for people, prioritizing showing things that matter for them) // [?] https://en.wikipedia.org/wiki/UTM_parameters