From 60fe5b2e4a5d57158f84a3b7e9bc69a82b564013 Mon Sep 17 00:00:00 2001 From: Olzhas Askar Date: Tue, 14 Oct 2025 14:55:33 +0200 Subject: [PATCH] Merge pull request #29043 from overleaf/oa-odc-to-cio [web] Send onboarding data to Customer IO GitOrigin-RevId: b45d1b66d36c3bba36e9c777fb66699c6bb56d33 --- .../Project/ProjectListController.mjs | 23 +++++++++++++++++++ services/web/app/views/_customer_io.pug | 10 +++++++- .../Project/ProjectListController.test.mjs | 23 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/services/web/app/src/Features/Project/ProjectListController.mjs b/services/web/app/src/Features/Project/ProjectListController.mjs index 348d00f601..0c5c5992be 100644 --- a/services/web/app/src/Features/Project/ProjectListController.mjs +++ b/services/web/app/src/Features/Project/ProjectListController.mjs @@ -29,6 +29,7 @@ import TutorialHandler from '../Tutorial/TutorialHandler.mjs' import SubscriptionHelper from '../Subscription/SubscriptionHelper.js' import PermissionsManager from '../Authorization/PermissionsManager.mjs' import AnalyticsManager from '../Analytics/AnalyticsManager.js' +import { OnboardingDataCollection } from '../../models/OnboardingDataCollection.js' /** * @import { GetProjectsRequest, GetProjectsResponse, AllUsersProjects, MongoProject, FormattedProject, MongoTag } from "./types" @@ -463,6 +464,12 @@ async function projectListPage(req, res, next) { affiliation => affiliation.institution?.enterpriseCommons ) + let onboardingDataCollection + let subjectArea + let usedLatex + let primaryOccupation + let role + // customer.io: Premium nudge experiment // Only do customer-io-trial-conversion assignment for users not in India/China and not in group/commons let customerIoEnabled = false @@ -482,6 +489,18 @@ async function projectListPage(req, res, next) { ) if (cioAssignment.variant === 'enabled') { customerIoEnabled = true + onboardingDataCollection = await OnboardingDataCollection.findById( + userId, + 'subjectArea usedLatex primaryOccupation role' + ) + + if (onboardingDataCollection) { + subjectArea = onboardingDataCollection.subjectArea + usedLatex = onboardingDataCollection.usedLatex + primaryOccupation = onboardingDataCollection.primaryOccupation + role = onboardingDataCollection.role + } + AnalyticsManager.setUserPropertyForUserInBackground( userId, 'customer-io-integration', @@ -541,6 +560,10 @@ async function projectListPage(req, res, next) { signUpDate: user.signUpDate ? Math.floor(user.signUpDate.getTime() / 1000) : null, + subjectArea, + primaryOccupation, + role, + usedLatex, }) } diff --git a/services/web/app/views/_customer_io.pug b/services/web/app/views/_customer_io.pug index 91c92d7836..c8361a001f 100644 --- a/services/web/app/views/_customer_io.pug +++ b/services/web/app/views/_customer_io.pug @@ -3,7 +3,7 @@ if(customerIoEnabled && ExposedSettings.cioWriteKey && ExposedSettings.cioSiteId function boolAttr(value) { return value !== undefined ? String(value) : null; } - script(type="text/javascript", id="cio-loader", nonce=scriptNonce, data-best-subscription=(usersBestSubscription && usersBestSubscription.type), data-ai-blocked=boolAttr(aiBlocked), data-has-ai-assist=boolAttr(hasAiAssist), data-cio-write-key=ExposedSettings.cioWriteKey, data-cio-site-id=ExposedSettings.cioSiteId, data-session-analytics-id=getSessionAnalyticsId(), data-user-id=getLoggedInUserId(), data-last-active=lastActive, data-sign-up-date=signUpDate). + script(type="text/javascript", id="cio-loader", nonce=scriptNonce, data-best-subscription=(usersBestSubscription && usersBestSubscription.type), data-ai-blocked=boolAttr(aiBlocked), data-has-ai-assist=boolAttr(hasAiAssist), data-cio-write-key=ExposedSettings.cioWriteKey, data-cio-site-id=ExposedSettings.cioSiteId, data-session-analytics-id=getSessionAnalyticsId(), data-user-id=getLoggedInUserId(), data-last-active=lastActive, data-sign-up-date=signUpDate, data-subject-area=subjectArea, data-role=role, data-used-latex=usedLatex, data-primary-occupation=primaryOccupation). function parseBool(value) { return value === 'true' ? true : value === 'false' ? false : undefined; @@ -19,6 +19,10 @@ if(customerIoEnabled && ExposedSettings.cioWriteKey && ExposedSettings.cioSiteId var hasAiAssist = parseBool(cioSettings.hasAiAssist); var lastActive = cioSettings.lastActive; var signUpDate = cioSettings.signUpDate; + var subjectArea = cioSettings.subjectArea; + var role = cioSettings.role; + var primaryOccupation = cioSettings.primaryOccupation; + var usedLatex = cioSettings.usedLatex; !function(){var i="cioanalytics", analytics=(window[i]=window[i]||[]);if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};for(var e=0;e ({ + OnboardingDataCollection: ctx.OnboardingDataCollectionModel, + })) + vi.doMock('../../../../app/src/Features/Project/ProjectGetter', () => ({ default: ctx.ProjectGetter, }))