From 8227199dcb45b9f1dc95f762b99d6d9eea7d20ae Mon Sep 17 00:00:00 2001 From: M Fahru Date: Fri, 20 Jan 2023 06:52:41 -0700 Subject: [PATCH] Implement new users micro survey for project dashboard page on react and non-react version (#11347) * Implement new users micro survey for project dashboard page on react and non-react version * Add 'new-users-micro-survey-click' and 'new-users-micro-survey-prompt' new event for the analytics GitOrigin-RevId: 9aabe931987638b38995d404aa90ac4d40b2f3b5 --- .../src/Features/Project/ProjectController.js | 32 +++++++++ .../Features/Project/ProjectListController.js | 27 +++++++ services/web/app/views/project/list-react.pug | 1 + .../app/views/project/list/notifications.pug | 30 ++++++++ .../web/frontend/extracted-translations.json | 2 + .../notifications/new-users-micro-survey.tsx | 72 +++++++++++++++++++ .../notifications/user-notifications.tsx | 2 + .../project-list/notifications-controller.js | 16 +++++ services/web/locales/en.json | 2 + 9 files changed, 184 insertions(+) create mode 100644 services/web/frontend/js/features/project-list/components/notifications/new-users-micro-survey.tsx diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index 5a87c67767..e69c2f26de 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -556,6 +556,26 @@ const ProjectController = { } ) }, + newUsersMicroSurveyAssignment(cb) { + SplitTestHandler.getAssignment( + req, + res, + 'new-users-micro-survey', + (err, assignment) => { + if (err) { + logger.error( + { err }, + 'failed to get "new-users-micro-survey" split test assignment' + ) + + const defaultAssignment = { variant: 'default' } + cb(null, defaultAssignment) + } else { + cb(null, assignment) + } + } + ) + }, survey(cb) { SurveyHandler.getSurvey(userId, (err, survey) => { if (err) { @@ -578,6 +598,7 @@ const ProjectController = { user, userEmailsData, groupsAndEnterpriseBannerAssignment, + newUsersMicroSurveyAssignment, userIsMemberOfGroupSubscription, } = results @@ -718,6 +739,16 @@ const ProjectController = { !userIsMemberOfGroupSubscription && !hasPaidAffiliation + const SEVEN_DAYS = 1000 * 60 * 60 * 24 * 7 + + const isUserLessThanSevenDaysOld = + user.signUpDate && Date.now() - user.signUpDate.getTime() < SEVEN_DAYS + + const showNewUsersMicroSurvey = + Features.hasFeature('saas') && + newUsersMicroSurveyAssignment.variant === 'enabled' && + isUserLessThanSevenDaysOld + ProjectController._injectProjectUsers(projects, (error, projects) => { if (error != null) { return next(error) @@ -745,6 +776,7 @@ const ProjectController = { showGroupsAndEnterpriseBanner, groupsAndEnterpriseBannerVariant: groupsAndEnterpriseBannerAssignment.variant, + showNewUsersMicroSurvey, } const paidUser = diff --git a/services/web/app/src/Features/Project/ProjectListController.js b/services/web/app/src/Features/Project/ProjectListController.js index a3d2cb8aa6..6c0c37cfe9 100644 --- a/services/web/app/src/Features/Project/ProjectListController.js +++ b/services/web/app/src/Features/Project/ProjectListController.js @@ -312,6 +312,32 @@ async function projectListReactPage(req, res, next) { !userIsMemberOfGroupSubscription && !hasPaidAffiliation + let newUsersMicroSurveyAssignment + + try { + newUsersMicroSurveyAssignment = + await SplitTestHandler.promises.getAssignment( + req, + res, + 'new-users-micro-survey' + ) + } catch (error) { + logger.error( + { err: error }, + 'failed to get "new-users-micro-survey" split test assignment' + ) + } + + const SEVEN_DAYS = 1000 * 60 * 60 * 24 * 7 + + const isUserLessThanSevenDaysOld = + user.signUpDate && Date.now() - user.signUpDate.getTime() < SEVEN_DAYS + + const showNewUsersMicroSurvey = + Features.hasFeature('saas') && + (newUsersMicroSurveyAssignment?.variant ?? 'default') === 'enabled' && + isUserLessThanSevenDaysOld + res.render('project/list-react', { title: 'your_projects', usersBestSubscription, @@ -329,6 +355,7 @@ async function projectListReactPage(req, res, next) { showGroupsAndEnterpriseBanner, groupsAndEnterpriseBannerVariant: groupsAndEnterpriseBannerAssignment?.variant ?? 'default', + showNewUsersMicroSurvey, }) } diff --git a/services/web/app/views/project/list-react.pug b/services/web/app/views/project/list-react.pug index ca30d22469..80dacc77bc 100644 --- a/services/web/app/views/project/list-react.pug +++ b/services/web/app/views/project/list-react.pug @@ -27,6 +27,7 @@ block append meta meta(name="ol-currentUrl" data-type="string" content=currentUrl) meta(name="ol-showGroupsAndEnterpriseBanner" data-type="boolean" content=showGroupsAndEnterpriseBanner) meta(name="ol-groupsAndEnterpriseBannerVariant" data-type="string" content=groupsAndEnterpriseBannerVariant) + meta(name="ol-showNewUsersMicroSurvey" data-type="boolean" content=showNewUsersMicroSurvey) block content main.content.content-alt.project-list-react#project-list-root diff --git a/services/web/app/views/project/list/notifications.pug b/services/web/app/views/project/list/notifications.pug index d7cba28192..2711873948 100644 --- a/services/web/app/views/project/list/notifications.pug +++ b/services/web/app/views/project/list/notifications.pug @@ -283,3 +283,33 @@ include ../../_mixins/reconfirm_affiliation button(ng-click="dismiss()").close.pull-right span(aria-hidden="true") × span.sr-only #{translate("close")} + + if showNewUsersMicroSurvey + ul.list-unstyled( + ng-controller="NewUsersMicroSurveyController", + ng-cloak + ) + li.notification-entry( + ng-if="!hasDismissedNewUsersMicroSurvey" + event-tracking="new-users-micro-survey-prompt" + event-tracking-mb="true" + event-tracking-trigger="load" + event-segmentation='{"project-dashboard-react": "default"}' + ) + .alert.alert-info + .notification-body + | !{translate("help_us_improve_overleaf_by_answering_a_two_question_survey", {}, ['strong'])} + .notification-action + a.pull-right.btn.btn-sm.btn-info( + ng-click="dismiss()" + href="https://docs.google.com/forms/d/e/1FAIpQLSdN23eSbaGkl96-LkNiIW1QCVdhAQEnSGrEhbuuZgNQ5-Qvog/viewform?usp=sf_link" + target="_blank" + event-tracking="new-users-micro-survey-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default"}' + ) #{translate("take_survey")} + .notification-close + button(ng-click="dismiss()").close.pull-right + span(aria-hidden="true") × + span.sr-only #{translate("close")} diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 3e6350f58e..f963f6620e 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -291,6 +291,7 @@ "have_an_extra_backup": "", "headers": "", "help": "", + "help_us_improve_overleaf_by_answering_a_two_question_survey": "", "hide_outline": "", "history": "", "hotkey_add_a_comment": "", @@ -668,6 +669,7 @@ "tags": "", "tags_slash_folders": "", "take_short_survey": "", + "take_survey": "", "template_approved_by_publisher": "", "templates": "", "terminated": "", diff --git a/services/web/frontend/js/features/project-list/components/notifications/new-users-micro-survey.tsx b/services/web/frontend/js/features/project-list/components/notifications/new-users-micro-survey.tsx new file mode 100644 index 0000000000..51350f54a7 --- /dev/null +++ b/services/web/frontend/js/features/project-list/components/notifications/new-users-micro-survey.tsx @@ -0,0 +1,72 @@ +import { useCallback, useEffect, useState } from 'react' +import Notification from './notification' +import { sendMB } from '../../../../infrastructure/event-tracking' +import getMeta from '../../../../utils/meta' +import customLocalStorage from '../../../../infrastructure/local-storage' +import { Trans, useTranslation } from 'react-i18next' + +export default function NewUsersMicroSurvey() { + const { t } = useTranslation() + + const showNewUsersMicroSurvey = getMeta( + 'ol-showNewUsersMicroSurvey' + ) as boolean + + const hasDismissedNewUsersMicroSurvey = customLocalStorage.getItem( + 'has_dismissed_new_users_micro_survey' + ) + + // need extra state to close the survey when user clicking the main button + const [show, setShow] = useState(!hasDismissedNewUsersMicroSurvey) + + const handleClose = useCallback(() => { + customLocalStorage.setItem('has_dismissed_new_users_micro_survey', true) + }, []) + + const handleClickTakeSurvey = useCallback(() => { + customLocalStorage.setItem('has_dismissed_new_users_micro_survey', true) + + setShow(false) + + sendMB('new-users-micro-survey-click', { + 'project-dashboard-react': 'enabled', + }) + }, []) + + useEffect(() => { + sendMB('new-users-micro-survey-prompt', { + 'project-dashboard-react': 'enabled', + }) + }, []) + + if (hasDismissedNewUsersMicroSurvey || !showNewUsersMicroSurvey || !show) { + return null + } + + return ( + + + + ] + } + /> + + + + + {t('take_survey')} + + + + ) +} diff --git a/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx b/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx index 948e9c0613..d1d4fa283b 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx @@ -3,6 +3,7 @@ import Institution from './groups/institution' import ConfirmEmail from './groups/confirm-email' import ReconfirmationInfo from './groups/affiliation/reconfirmation-info' import GroupsAndEnterpriseBanner from './groups-and-enterprise-banner' +import NewUsersMicroSurvey from './new-users-micro-survey' function UserNotifications() { return ( @@ -13,6 +14,7 @@ function UserNotifications() { + ) diff --git a/services/web/frontend/js/main/project-list/notifications-controller.js b/services/web/frontend/js/main/project-list/notifications-controller.js index 31823fad5f..e6b56a4235 100644 --- a/services/web/frontend/js/main/project-list/notifications-controller.js +++ b/services/web/frontend/js/main/project-list/notifications-controller.js @@ -47,6 +47,22 @@ App.controller( } ) +App.controller( + 'NewUsersMicroSurveyController', + function ($scope, localStorage) { + $scope.hasDismissedNewUsersMicroSurvey = localStorage( + 'has_dismissed_new_users_micro_survey' + ) + + $scope.dismiss = () => { + localStorage('has_dismissed_new_users_micro_survey', true) + $scope.hasDismissedNewUsersMicroSurvey = true + } + + $scope.newUsersMicroSurveyVariant = getMeta('ol-newUsersMicroSurvey') + } +) + App.controller('ProjectInviteNotificationController', function ($scope, $http) { // Shortcuts for translation keys $scope.projectName = $scope.notification.messageOpts.projectName diff --git a/services/web/locales/en.json b/services/web/locales/en.json index a2b810caf7..04718ee38b 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -632,6 +632,7 @@ "help": "Help", "help_articles_matching": "Help articles matching your subject", "help_improve_overleaf_fill_out_this_survey": "If you would like to help us improve Overleaf, please take a moment to fill out <0>this survey.", + "help_us_improve_overleaf_by_answering_a_two_question_survey": "Help us improve __appName__ by answering a <0>two question survey.", "help_us_spread_word": "Help us spread the word about __appName__", "hide_outline": "Hide File outline", "history": "History", @@ -1415,6 +1416,7 @@ "tags_slash_folders": "Tags/Folders", "take_me_home": "Take me home!", "take_short_survey": "Take a short survey", + "take_survey": "Take Survey", "tc_everyone": "Everyone", "tc_guests": "Guests", "tc_switch_everyone_tip": "Toggle track-changes for everyone",