Merge onboarding experiments integration branch (#9571)

* Add onboarding survey page

* Add onboarding new analytics events to mixpanel allowlist

* [web] Try Premium prompt

* moved try-premium code to overleaf-integration

* fixed sheet styling

* Add onboarding flow redirect handling to registration handlers (#9462)

* Add redirect logic for onboarding flow after registration

* Update UPGRADE_PROMPT_URL

* Cleanup style of OverleafAuthenticationController

* Refactor finishLogin calls to call wrapped function in OverleafAuthenticationController

* Refactor user properties/onboarding flow redirect into finishLogin wrapper

* Fix async/await calls after refactoring

* Update tests for finishLogin refactoring

* Don't redirect to upgrade prompt if user has premium via commons

Co-authored-by: Miguel Serrano <mserranom@users.noreply.github.com>
GitOrigin-RevId: 7c392aa6949f99fa909f9ca4e4baad4c4d4ff6be
This commit is contained in:
Thomas 2022-09-14 10:32:16 +02:00 committed by Copybot
parent a0fabee3b4
commit db9fad7cf8
14 changed files with 203 additions and 139 deletions

View file

@ -263,6 +263,8 @@ async function interstitialPaymentPage(req, res) {
const hasSubscription =
await LimitationsManager.promises.userHasV1OrV2Subscription(user)
const showSkipLink = req.query?.skipLink === 'true'
if (hasSubscription) {
res.redirect('/user/subscription?hasSubscription=true')
} else {
@ -272,6 +274,7 @@ async function interstitialPaymentPage(req, res) {
itm_campaign: req.query?.itm_campaign,
recommendedCurrency,
interstitialPaymentConfig,
showSkipLink,
})
}
}
@ -339,6 +342,8 @@ async function successfulSubscription(req, res) {
const premiumFeaturesDiscoverability =
premiumFeaturesDiscoverabilityAssignment?.variant === 'active'
const postCheckoutRedirect = req.session?.postCheckoutRedirect
if (!personalSubscription) {
res.redirect('/user/subscription/plans')
} else {
@ -346,6 +351,7 @@ async function successfulSubscription(req, res) {
title: 'thank_you',
personalSubscription,
premiumFeaturesDiscoverability,
postCheckoutRedirect,
})
}
}

View file

@ -181,6 +181,7 @@ const UserSchema = new Schema({
auditLog: [AuditLogEntrySchema],
splitTests: Schema.Types.Mixed,
analyticsId: { type: String },
surveyResponses: Schema.Types.Mixed,
})
function formatSplitTestsSchema(next) {

View file

@ -1,13 +1,14 @@
nav.navbar.navbar-default.navbar-main
.container-fluid
.navbar-header
button.navbar-toggle.collapsed(
type="button",
data-toggle="collapse",
data-target="[data-ol-navbar-main-collapse]"
aria-label="Toggle " + translate('navigation')
)
i.fa.fa-bars(aria-hidden="true")
if (typeof(suppressNavbarRight) == "undefined")
button.navbar-toggle.collapsed(
type="button",
data-toggle="collapse",
data-target="[data-ol-navbar-main-collapse]"
aria-label="Toggle " + translate('navigation')
)
i.fa.fa-bars(aria-hidden="true")
if settings.nav.custom_logo
a(href='/', aria-label=settings.appName, style='background-image:url("'+settings.nav.custom_logo+'")').navbar-brand
else if (nav.title)
@ -20,72 +21,85 @@ nav.navbar.navbar-default.navbar-main
- var canDisplaySplitTestMenu = hasFeature('saas') && (canDisplayAdminMenu || (getSessionUser() && getSessionUser().staffAccess && (getSessionUser().staffAccess.splitTestMetrics || getSessionUser().staffAccess.splitTestManagement)))
- var canDisplaySurveyMenu = hasFeature('saas') && canDisplayAdminMenu
.navbar-collapse.collapse(data-ol-navbar-main-collapse)
ul.nav.navbar-nav.navbar-right
if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu)
li.dropdown.subdued
a.dropdown-toggle(
href="#",
role="button",
aria-haspopup="true",
aria-expanded="false",
data-toggle="dropdown"
)
| Admin
span.caret
ul.dropdown-menu
if canDisplayAdminMenu
li
a(href="/admin") Manage Site
li
a(href="/admin/user") Manage Users
li
a(href="/admin/project") Project URL Lookup
if canDisplayAdminRedirect
li
a(href=settings.adminUrl) Switch to Admin
if canDisplaySplitTestMenu
li
a(href="/admin/split-test") Manage Split Tests
if canDisplaySurveyMenu
li
a(href="/admin/survey") Manage Surveys
if (typeof(suppressNavbarRight) == "undefined")
.navbar-collapse.collapse(data-ol-navbar-main-collapse)
ul.nav.navbar-nav.navbar-right
if (canDisplayAdminMenu || canDisplayAdminRedirect || canDisplaySplitTestMenu)
li.dropdown.subdued
a.dropdown-toggle(
href="#",
role="button",
aria-haspopup="true",
aria-expanded="false",
data-toggle="dropdown"
)
| Admin
span.caret
ul.dropdown-menu
if canDisplayAdminMenu
li
a(href="/admin") Manage Site
li
a(href="/admin/user") Manage Users
li
a(href="/admin/project") Project URL Lookup
if canDisplayAdminRedirect
li
a(href=settings.adminUrl) Switch to Admin
if canDisplaySplitTestMenu
li
a(href="/admin/split-test") Manage Split Tests
if canDisplaySurveyMenu
li
a(href="/admin/survey") Manage Surveys
// loop over header_extras
each item in ((splitTestVariants && (splitTestVariants['unified-navigation'] === 'show-unified-navigation')) ? nav.header_extras_unified : nav.header_extras)
-
if ((item.only_when_logged_in && getSessionUser())
|| (item.only_when_logged_out && (!getSessionUser()))
|| (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages)
|| (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks))
){
var showNavItem = true
} else {
var showNavItem = false
}
// loop over header_extras
each item in ((splitTestVariants && (splitTestVariants['unified-navigation'] === 'show-unified-navigation')) ? nav.header_extras_unified : nav.header_extras)
-
if ((item.only_when_logged_in && getSessionUser())
|| (item.only_when_logged_out && (!getSessionUser()))
|| (!item.only_when_logged_out && !item.only_when_logged_in && !item.only_content_pages)
|| (item.only_content_pages && (typeof(suppressNavContentLinks) == "undefined" || !suppressNavContentLinks))
){
var showNavItem = true
} else {
var showNavItem = false
}
if showNavItem
if item.dropdown
li.dropdown(class=item.class)
a.dropdown-toggle(
href="#",
role="button",
aria-haspopup="true",
aria-expanded="false",
data-toggle="dropdown"
)
| !{translate(item.text)}
span.caret
ul.dropdown-menu
each child in item.dropdown
if child.divider
li.divider
if child.splitTest
if (splitTestVariants && (splitTestVariants[child.splitTest.name] === child.splitTest.variant))
if showNavItem
if item.dropdown
li.dropdown(class=item.class)
a.dropdown-toggle(
href="#",
role="button",
aria-haspopup="true",
aria-expanded="false",
data-toggle="dropdown"
)
| !{translate(item.text)}
span.caret
ul.dropdown-menu
each child in item.dropdown
if child.divider
li.divider
if child.splitTest
if (splitTestVariants && (splitTestVariants[child.splitTest.name] === child.splitTest.variant))
li
if child.url
a(
href=child.url,
class=child.class,
event-tracking=child.event
event-tracking-mb="true"
event-tracking-trigger="click"
) !{translate(child.text)}
else
| !{translate(child.text)}
else
li
if child.url
a(
href=child.url,
href=child.url,
class=child.class,
event-tracking=child.event
event-tracking-mb="true"
@ -93,81 +107,69 @@ nav.navbar.navbar-default.navbar-main
) !{translate(child.text)}
else
| !{translate(child.text)}
else
li
if child.url
a(
href=child.url,
class=child.class,
event-tracking=child.event
event-tracking-mb="true"
event-tracking-trigger="click"
) !{translate(child.text)}
else
| !{translate(child.text)}
else
li(class=item.class)
if item.url
a(
href=item.url,
class=item.class,
event-tracking=item.event
event-tracking-mb="true"
event-tracking-trigger="click"
) !{translate(item.text)}
else
| !{translate(item.text)}
else
li(class=item.class)
if item.url
a(
href=item.url,
class=item.class,
event-tracking=item.event
event-tracking-mb="true"
event-tracking-trigger="click"
) !{translate(item.text)}
else
| !{translate(item.text)}
// logged out
if !getSessionUser()
// register link
if hasFeature('registration-page')
// logged out
if !getSessionUser()
// register link
if hasFeature('registration-page')
li
a(
href="/register"
event-tracking="menu-clicked-register"
event-tracking-action="clicked"
event-tracking-trigger="click"
event-tracking-mb="true"
event-segmentation={ page: currentUrl }
) #{translate('register')}
// login link
li
a(
href="/register"
event-tracking="menu-clicked-register"
href="/login"
event-tracking="menu-clicked-login"
event-tracking-action="clicked"
event-tracking-trigger="click"
event-tracking-mb="true"
event-segmentation={ page: currentUrl }
) #{translate('register')}
) #{translate('log_in')}
// login link
li
a(
href="/login"
event-tracking="menu-clicked-login"
event-tracking-action="clicked"
event-tracking-trigger="click"
event-tracking-mb="true"
event-segmentation={ page: currentUrl }
) #{translate('log_in')}
// projects link and account menu
if getSessionUser()
li
a(href="/project") #{translate('Projects')}
li.dropdown
a.dropdown-toggle(
href="#",
role="button",
aria-haspopup="true",
aria-expanded="false",
data-toggle="dropdown"
)
| #{translate('Account')}
span.caret
ul.dropdown-menu
li
div.subdued #{getSessionUser().email}
li.divider.hidden-xs.hidden-sm
li
a(href="/user/settings") #{translate('Account Settings')}
if nav.showSubscriptionLink
// projects link and account menu
if getSessionUser()
li
a(href="/project") #{translate('Projects')}
li.dropdown
a.dropdown-toggle(
href="#",
role="button",
aria-haspopup="true",
aria-expanded="false",
data-toggle="dropdown"
)
| #{translate('Account')}
span.caret
ul.dropdown-menu
li
a(href="/user/subscription") #{translate('subscription')}
li.divider.hidden-xs.hidden-sm
li
form(method="POST" action="/logout")
input(name='_csrf', type='hidden', value=csrfToken)
button.btn-link.text-left.dropdown-menu-button #{translate('log_out')}
div.subdued #{getSessionUser().email}
li.divider.hidden-xs.hidden-sm
li
a(href="/user/settings") #{translate('Account Settings')}
if nav.showSubscriptionLink
li
a(href="/user/subscription") #{translate('subscription')}
li.divider.hidden-xs.hidden-sm
li
form(method="POST" action="/logout")
input(name='_csrf', type='hidden', value=csrfToken)
button.btn-link.text-left.dropdown-menu-button #{translate('log_out')}

View file

@ -41,5 +41,15 @@ block content
//- sticky header on mobile will be "hidden" (by removing its sticky position) if it reaches this div
.invisible(aria-hidden="true" data-ol-plans-v2-table-sticky-header-stop)
if (showSkipLink)
.row.row-spaced-small.text-center
a(href='/project'
event-tracking="skip-button-click"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"paywall-type": "interstitial-page"}'
)
| #{translate("continue_with_free_plan")}
!= moduleIncludes("contactModalGeneral-marketing", locals)

View file

@ -30,4 +30,7 @@ block content
br(ng-non-bindable)
| The #{settings.appName} Team
p
a.btn.btn-primary(href="/project") &lt; #{translate("back_to_your_projects")}
if (postCheckoutRedirect)
a.btn.btn-primary(href=postCheckoutRedirect) &lt; #{translate("back_to_your_projects")}
else
a.btn.btn-primary(href="/project") &lt; #{translate("back_to_your_projects")}

View file

@ -21,3 +21,7 @@
}
}
}
.list-style-check-green {
list-style-image: url('../../../public/img/fa-check-green.svg');
}

View file

@ -667,6 +667,8 @@
"dropbox_sync_error": "Sorry, there was a problem checking our Dropbox service. Please try again in a few moments.",
"send": "Send",
"sending": "Sending",
"continue": "Continue",
"skip": "Skip",
"invalid_password": "Invalid Password",
"invalid_password_not_set": "Password is required",
"invalid_password_too_short": "Password too short, minimum __minLength__",
@ -1836,6 +1838,17 @@
"server_pro_license_entitlement_line_1": "<0>__appName__</0> Server Pro license",
"server_pro_license_entitlement_line_2": "You currently have <0>__count__ active users</0>. If you need to increase your license entitlement, please <1>contact Overleaf</1>.",
"server_pro_license_entitlement_line_3": "An active user is one who has opened a project in this Server Pro instance in the last 12 months.",
"get_the_most_out_headline": "Get the most out of __appName__ with features such as:",
"more_project_collaborators": "<0>More</0> project <0>collaborators</0>",
"advanced_reference_search": "Advanced <0>reference search</0>",
"longer_compile_timeout": "Longer <0>compile timeout</0>",
"symbol_palette_highlighted": "<0>Symbol</0> palette",
"real_time_track_changes": "Real-time <0>track-changes</0>",
"full_document_history": "Full document <0>history</0>",
"github_git_and_dropbox_integrations": "<0>Github</0>, <0>Git</0> and <0>Dropbox</0> integrations",
"zotero_and_mendeley_integrations": "<0>Zotero</0> and <0>Mendeley</0> integrations",
"skip": "Skip",
"continue_with_free_plan": "Continue with free plan",
"history_entry_origin_upload": "upload",
"history_entry_origin_git": "via Git",
"history_entry_origin_github": "via GitHub",

View file

@ -0,0 +1,3 @@
<svg width="14" height="10" viewBox="0 0 14 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.0547 2.42187C13.0547 2.22656 12.9766 2.03125 12.8359 1.89063L11.7734 0.828125C11.6328 0.6875 11.4375 0.609375 11.2422 0.609375C11.0469 0.609375 10.8516 0.6875 10.7109 0.828125L5.58594 5.96094L3.28906 3.65625C3.14844 3.51563 2.95313 3.4375 2.75781 3.4375C2.5625 3.4375 2.36719 3.51563 2.22656 3.65625L1.16406 4.71875C1.02344 4.85937 0.945312 5.05469 0.945312 5.25C0.945312 5.44531 1.02344 5.64063 1.16406 5.78125L5.05469 9.67188C5.19531 9.8125 5.39063 9.89062 5.58594 9.89062C5.78125 9.89062 5.97656 9.8125 6.11719 9.67188L12.8359 2.95313C12.9766 2.8125 13.0547 2.61719 13.0547 2.42187Z" fill="#0F7A06"/>
</svg>

After

Width:  |  Height:  |  Size: 722 B

View file

@ -0,0 +1,15 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" fill="white"/>
<g clip-path="url(#clip0_904_845)">
<path d="M10.8329 4.33325L1.66667 10.1731L10.8329 16.013L20.0008 10.1731L10.8329 4.33325Z" fill="#0061FF"/>
<path d="M29.1671 4.33325L20.0008 10.1731L29.1671 16.013L38.3333 10.1731L29.1671 4.33325Z" fill="#0061FF"/>
<path d="M1.66667 21.8528L10.8329 27.6927L20.0008 21.8528L10.8329 16.0129L1.66667 21.8528Z" fill="#0061FF"/>
<path d="M29.1671 16.0129L20.0008 21.8528L29.1671 27.6927L38.3333 21.8528L29.1671 16.0129Z" fill="#0061FF"/>
<path d="M10.8329 29.6394L20.0008 35.4793L29.167 29.6394L20.0008 23.7996L10.8329 29.6394Z" fill="#0061FF"/>
</g>
<defs>
<clipPath id="clip0_904_845">
<rect width="36.6667" height="31.146" fill="white" transform="translate(1.66667 4.33325)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 923 B

View file

@ -0,0 +1,3 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M35.2989 16.4138L19.59 0.706825C18.6861 -0.197848 17.219 -0.197848 16.314 0.706825L13.0522 3.96893L17.1897 8.1064C18.1515 7.78165 19.254 7.99939 20.0204 8.7659C20.7907 9.53718 21.0069 10.6491 20.674 11.6142L24.6618 15.6019C25.6266 15.2695 26.7397 15.4843 27.5101 16.256C28.5871 17.3327 28.5871 19.0774 27.5101 20.1544C26.4329 21.2318 24.6882 21.2318 23.6104 20.1544C22.8005 19.344 22.6002 18.1541 23.0104 17.1563L19.2914 13.4372L19.291 23.2238C19.5536 23.3538 19.8014 23.5273 20.0202 23.7452C21.0969 24.8218 21.0969 26.5662 20.0202 27.6445C18.9432 28.7211 17.1977 28.7211 16.1218 27.6445C15.0449 26.5663 15.0449 24.8219 16.1218 23.7452C16.3879 23.4795 16.6959 23.2784 17.0245 23.1437V13.2661C16.6957 13.1319 16.3881 12.9323 16.1216 12.6645C15.3059 11.8495 15.1095 10.6523 15.5278 9.65065L11.4491 5.57128L0.678757 16.3413C-0.226201 17.2466 -0.226201 18.7138 0.678757 19.6185L16.3868 35.3256C17.2911 36.2303 18.7578 36.2303 19.6634 35.3256L35.2988 19.6927C36.2034 18.7876 36.2034 17.32 35.2988 16.4152" fill="#F05133"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,3 @@
<svg width="34" height="33" viewBox="0 0 34 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.665 -9.53674e-05C7.46238 -9.53674e-05 0 7.46063 0 16.6649C0 24.0277 4.77503 30.2744 11.3965 32.478C12.2294 32.6323 12.5352 32.1165 12.5352 31.6763C12.5352 31.2792 12.5197 29.9662 12.5125 28.5736C7.87633 29.5817 6.89803 26.6074 6.89803 26.6074C6.13996 24.6814 5.04768 24.169 5.04768 24.169C3.53567 23.1346 5.16166 23.1559 5.16166 23.1559C6.83511 23.2734 7.71627 24.8735 7.71627 24.8735C9.20262 27.4209 11.6148 26.6844 12.5658 26.2586C12.7157 25.1817 13.1473 24.4463 13.6239 24.0301C9.92234 23.609 6.03122 22.1798 6.03122 15.7942C6.03122 13.9751 6.68223 12.4882 7.74828 11.3211C7.57525 10.9014 7.00483 9.20639 7.91 6.91091C7.91 6.91091 9.30942 6.46329 12.4938 8.61941C13.8231 8.25017 15.2487 8.06499 16.665 8.05837C18.0813 8.06499 19.508 8.25017 20.8398 8.61941C24.0206 6.46329 25.4181 6.91091 25.4181 6.91091C26.3252 9.20639 25.7548 10.9014 25.5817 11.3211C26.6503 12.4882 27.2966 13.9751 27.2966 15.7942C27.2966 22.1953 23.398 23.6043 19.6871 24.0169C20.2848 24.5341 20.8175 25.5482 20.8175 27.103C20.8175 29.3328 20.7984 31.1274 20.7984 31.6763C20.7984 32.1201 21.0981 32.6394 21.9428 32.4761C28.561 30.27 33.33 24.0255 33.33 16.6649C33.33 7.46063 25.8687 -9.53674e-05 16.665 -9.53674e-05Z" fill="#1B1817"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View file

@ -404,6 +404,7 @@ describe('SubscriptionController', function () {
title: 'thank_you',
personalSubscription: 'foo',
premiumFeaturesDiscoverability: false,
postCheckoutRedirect: undefined,
})
done()
}