diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js
index 8e44c236bb..8feb25bb49 100644
--- a/services/web/app/src/Features/Subscription/SubscriptionController.js
+++ b/services/web/app/src/Features/Subscription/SubscriptionController.js
@@ -18,6 +18,7 @@ const AnalyticsManager = require('../Analytics/AnalyticsManager')
const RecurlyEventHandler = require('./RecurlyEventHandler')
const { expressify } = require('../../util/promises')
const OError = require('@overleaf/o-error')
+const SplitTestHandler = require('../SplitTests/SplitTestHandler')
const groupPlanModalOptions = Settings.groupPlanModalOptions
const validGroupPlanModalOptions = {
@@ -56,6 +57,14 @@ async function plansPage(req, res) {
AnalyticsManager.recordEventForSession(req.session, 'plans-page-view')
+ const assignment = await SplitTestHandler.promises.getAssignment(
+ req,
+ 'plans-page-layout'
+ )
+
+ const newPlansPageVariant =
+ assignment && assignment.variant === 'new-plans-page'
+
res.render('subscriptions/plans-marketing', {
title: 'plans_and_pricing',
plans,
@@ -66,6 +75,7 @@ async function plansPage(req, res) {
groupPlans: GroupPlansData,
groupPlanModalOptions,
groupPlanModalDefaults,
+ newPlansPageVariant,
})
}
diff --git a/services/web/app/views/subscriptions/plans-marketing.pug b/services/web/app/views/subscriptions/plans-marketing.pug
index e69c219f68..58c71bbe43 100644
--- a/services/web/app/views/subscriptions/plans-marketing.pug
+++ b/services/web/app/views/subscriptions/plans-marketing.pug
@@ -34,53 +34,94 @@ block content
.col-md-8.col-md-offset-2
p.text-centered #{translate("sl_benefits_plans")}
- +allCardsAndControls()
+ if (newPlansPageVariant)
+ +allCardsAndControlsForVariant
+ else
+ +allCardsAndControls()
- .row.row-spaced-large.text-centered
- .col-xs-12
- p.text-centered !{translate('also_provides_free_plan', {}, [{ name: 'a', attrs: { href: '/register' }}])}
- i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true")
- span.sr-only Mastercard accepted
- i.fa.fa-cc-visa.fa-2x(aria-hidden="true")
- span.sr-only Visa accepted
- i.fa.fa-cc-amex.fa-2x(aria-hidden="true")
- span.sr-only Amex accepted
- i.fa.fa-cc-paypal.fa-2x(aria-hidden="true")
- span.sr-only Paypal accepted
- div.text-centered #{translate('change_plans_any_time')}
#{translate('billed_after_x_days', {len:'7'})}
- br
- div.text-centered #{translate('subject_to_additional_vat')}
#{translate('select_country_vat')}
+ if (newPlansPageVariant)
+ .row.features-table(event-tracking="features-table-viewed" event-tracking-ga="subscription-funnel" event-tracking-trigger="scroll" event-tracking-send-once="true" event-tracking-label="exp-")
+ .col-sm-12(data-ol-view='monthly')
+ +table_premium
+ .col-sm-12(hidden data-ol-view='annual')
+ +table_premium
+ .col-sm-12(hidden data-ol-view='student')
+ +table_student
- .row.row-spaced-large
- .col-md-8.col-md-offset-2
- .card.text-centered
- .card-header
- h2 #{translate('looking_multiple_licenses')}
- span #{translate('reduce_costs_group_licenses')}
+ .row.row-spaced-large
+ .col-md-8.col-md-offset-2
+ .card.text-centered
+ .card-header
+ h2 #{translate('looking_multiple_licenses')}
+ span #{translate('reduce_costs_group_licenses')}
+ br
+ br
+ a.btn.btn-default(
+ href="#groups"
+ data-ol-open-group-plan-modal
+ data-ol-location='callout'
+ ) #{translate('find_out_more')}
+
+ .row.row-spaced-large.text-centered
+ .col-xs-12
+ p.text-centered !{translate('also_provides_free_plan', {}, [{ name: 'a', attrs: { href: '/register' }}])}
+ i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true")
+ span.sr-only Mastercard accepted
+ i.fa.fa-cc-visa.fa-2x(aria-hidden="true")
+ span.sr-only Visa accepted
+ i.fa.fa-cc-amex.fa-2x(aria-hidden="true")
+ span.sr-only Amex accepted
+ i.fa.fa-cc-paypal.fa-2x(aria-hidden="true")
+ span.sr-only Paypal accepted
+ div.text-centered #{translate('change_plans_any_time')}
#{translate('billed_after_x_days', {len:'7'})}
br
+ div.text-centered #{translate('subject_to_additional_vat')}
#{translate('select_country_vat')}
+ else
+ .row.row-spaced-large.text-centered
+ .col-xs-12
+ p.text-centered !{translate('also_provides_free_plan', {}, [{ name: 'a', attrs: { href: '/register' }}])}
+ i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true")
+ span.sr-only Mastercard accepted
+ i.fa.fa-cc-visa.fa-2x(aria-hidden="true")
+ span.sr-only Visa accepted
+ i.fa.fa-cc-amex.fa-2x(aria-hidden="true")
+ span.sr-only Amex accepted
+ i.fa.fa-cc-paypal.fa-2x(aria-hidden="true")
+ span.sr-only Paypal accepted
+ div.text-centered #{translate('change_plans_any_time')}
#{translate('billed_after_x_days', {len:'7'})}
br
- a.btn.btn-default(
- href="#groups"
- data-ol-open-group-plan-modal
- data-ol-location='callout'
- ) #{translate('find_out_more')}
+ div.text-centered #{translate('subject_to_additional_vat')}
#{translate('select_country_vat')}
- .row.row-spaced-large
- .col-sm-12
- .page-header.plans-header.plans-subheader.text-centered
- h2 #{translate('compare_plan_features')}
- .row
- .col-md-6.col-md-offset-3
- +plan_switch('table')
- .col-md-3.text-right
- +currency_dropdown
- .row(event-tracking="features-table-viewed" event-tracking-ga="subscription-funnel" event-tracking-trigger="scroll" event-tracking-send-once="true" event-tracking-label="exp-")
- .col-sm-12(data-ol-view='monthly')
- +table_premium
- .col-sm-12(hidden data-ol-view='annual')
- +table_premium
- .col-sm-12(hidden data-ol-view='student')
- +table_student
+ .row.row-spaced-large
+ .col-md-8.col-md-offset-2
+ .card.text-centered
+ .card-header
+ h2 #{translate('looking_multiple_licenses')}
+ span #{translate('reduce_costs_group_licenses')}
+ br
+ br
+ a.btn.btn-default(
+ href="#groups"
+ data-ol-open-group-plan-modal
+ data-ol-location='callout'
+ ) #{translate('find_out_more')}
+
+ .row.row-spaced-large
+ .col-sm-12
+ .page-header.plans-header.plans-subheader.text-centered
+ h2 #{translate('compare_plan_features')}
+ .row
+ .col-md-6.col-md-offset-3
+ +plan_switch('table')
+ .col-md-3.text-right
+ +currency_dropdown
+ .row(event-tracking="features-table-viewed" event-tracking-ga="subscription-funnel" event-tracking-trigger="scroll" event-tracking-send-once="true" event-tracking-label="exp-")
+ .col-sm-12(data-ol-view='monthly')
+ +table_premium
+ .col-sm-12(hidden data-ol-view='annual')
+ +table_premium
+ .col-sm-12(hidden data-ol-view='student')
+ +table_student
include ./plans-marketing/_quotes
diff --git a/services/web/app/views/subscriptions/plans-marketing/_mixins.pug b/services/web/app/views/subscriptions/plans-marketing/_mixins.pug
index 8e621ce647..bc0b546fa8 100644
--- a/services/web/app/views/subscriptions/plans-marketing/_mixins.pug
+++ b/services/web/app/views/subscriptions/plans-marketing/_mixins.pug
@@ -65,6 +65,24 @@ mixin card_student_monthly(location)
span
+price_student_monthly
+features_student(location, 'monthly')
+mixin card_student_annual_variant(location)
+ .best-value
+ strong #{translate('best_value')}
+ .card-header
+ h2 #{translate("student")} (#{translate("annual")})
+ h5.tagline #{translate('tagline_student_annual')}
+ .circle
+ span
+ +price_student_annual
+ +btn_buy_student(location, 'annual')
+mixin card_student_monthly_variant(location)
+ .card-header
+ h2 #{translate("student")}
+ h5.tagline #{translate('tagline_student_monthly')}
+ .circle
+ span
+ +price_student_monthly
+ +btn_buy_student(location, 'monthly')
//- Features Lists, used within cards
mixin features_collaborator(location)
@@ -192,6 +210,27 @@ mixin plan_switch(location)
href="#"
) #{translate("special_price_student")}
+mixin plan_switch_variant()
+ ul.nav.nav-pills
+ li.active(data-ol-view-tab='monthly')
+ a.btn.btn-default-outline(
+ href="#"
+ ) #{translate("monthly")}
+ li(data-ol-view-tab='annual')
+ a.btn.btn-default-outline(
+ href="#"
+ ) #{translate("annual")}
+ li(data-ol-view-tab='student')
+ a.btn.btn-default-outline(
+ href="#"
+ ) #{translate("special_price_student")}
+ li()
+ a.btn.btn-default-outline(
+ href="#groups"
+ data-ol-open-group-plan-modal
+ data-ol-location='toggle'
+ ) #{translate("group_plans")}
+
mixin allCardsAndControls(controlsRowSpaced, listLocation)
- var location = listLocation ? 'card_' + listLocation : 'card'
.row.top-switch(class=(controlsRowSpaced ? "row-spaced" : ""))
@@ -248,3 +287,70 @@ mixin allCardsAndControls(controlsRowSpaced, listLocation)
.col-md-4
.card.card-last
+card_student_monthly(location)
+
+mixin allCardsAndControlsForVariant()
+ - var location = listLocation ? 'card_' + listLocation : 'card'
+ .row.top-switch(class=(controlsRowSpaced ? "row-spaced" : ""))
+ .col-lg-6.col-lg-offset-3.col-md-8.col-md-offset-1
+ +plan_switch_variant('card')
+ .col-md-2.text-right
+ +currency_dropdown
+
+ .row
+ .col-md-10.col-md-offset-1
+ .row
+ for view in ['monthly', 'annual']
+ .card-group.text-centered(data-ol-view=view hidden=(view==='annual'))
+ .col-md-4
+ .card.card-first
+ .card-header
+ h2 #{translate("personal")}
+ h5.tagline #{translate("tagline_personal")}
+ .circle
+ +price_personal
+ ul.list-unstyled
+ li #{translate("one_collaborator")}
+ +btn_buy_personal(location)
+ .col-md-4
+ .card.card-highlighted
+ .best-value
+ strong #{translate('best_value')}
+ .card-header
+ h2 #{translate("collaborator")}
+ h5.tagline #{translate("tagline_collaborator")}
+ .circle
+ +price_collaborator
+ ul.list-unstyled
+ li
+ strong #{translate("collabs_per_proj", {collabcount:10})}
+ +btn_buy_collaborator(location)
+ .col-md-4
+ .card.card-last
+ .card-header
+ h2 #{translate("professional")}
+ h5.tagline #{translate("tagline_professional")}
+ .circle
+ +price_professional
+ ul.list-unstyled
+ li
+ strong #{translate("unlimited_collabs")}
+ +btn_buy_professional(location)
+
+ .card-group.text-centered(hidden data-ol-view='student')
+ .col-md-4
+ .card.card-first
+ .card-header
+ h2 #{translate("free")}
+ h5.tagline #{translate("tagline_free")}
+ .circle #{translate("free")}
+ ul.list-unstyled
+ li #{translate("one_collaborator")}
+ +btn_buy_free(location)
+
+ .col-md-4
+ .card.card-highlighted
+ +card_student_annual_variant(location)
+
+ .col-md-4
+ .card.card-last
+ +card_student_monthly_variant(location)
diff --git a/services/web/frontend/js/features/plans/group-plan-modal/index.js b/services/web/frontend/js/features/plans/group-plan-modal/index.js
index 153ee16a90..ee5bab66fd 100644
--- a/services/web/frontend/js/features/plans/group-plan-modal/index.js
+++ b/services/web/frontend/js/features/plans/group-plan-modal/index.js
@@ -53,6 +53,11 @@ function updateGroupPlanView() {
).hidden = size >= 10
}
+function getGroupModalOpeningLocation() {
+ const modalEl = $('[data-ol-group-plan-modal]')
+ return modalEl.data('ol-modal-opening-location')
+}
+
const modalEl = $('[data-ol-group-plan-modal]')
modalEl
.on('shown.bs.modal', function () {
@@ -63,8 +68,11 @@ modalEl
history.replaceState(null, document.title, window.location.pathname)
})
-function showGroupPlanModal() {
+function showGroupPlanModal(location) {
modalEl.modal()
+ if (location) {
+ modalEl.data('ol-modal-opening-location', location)
+ }
eventTracking.send(
'subscription-funnel',
'plans-page',
@@ -93,6 +101,11 @@ document.querySelectorAll('[data-ol-purchase-group-plan]').forEach(el =>
const itmContent = getMeta('ol-itm_content')
if (itmContent) {
queryParams.set('itm_content', itmContent)
+ } else {
+ const openingLocation = getGroupModalOpeningLocation()
+ if (openingLocation === 'toggle') {
+ queryParams.set('itm_content', openingLocation)
+ }
}
eventTracking.sendMB('groups-modal-click', {
plan: planCode,
@@ -115,7 +128,7 @@ document.querySelectorAll('[data-ol-open-group-plan-modal]').forEach(el => {
location,
period: 'annual',
})
- showGroupPlanModal()
+ showGroupPlanModal(location)
})
})
diff --git a/services/web/frontend/stylesheets/app/plans.less b/services/web/frontend/stylesheets/app/plans.less
index a961e61fd5..06ec8e61e2 100644
--- a/services/web/frontend/stylesheets/app/plans.less
+++ b/services/web/frontend/stylesheets/app/plans.less
@@ -132,6 +132,9 @@
margin-left: @line-height-computed / 2;
}
}
+ .features-table {
+ margin-top: 20px;
+ }
}
#changePlanSection {
diff --git a/services/web/locales/en.json b/services/web/locales/en.json
index cb94d4162d..a8fc8be990 100644
--- a/services/web/locales/en.json
+++ b/services/web/locales/en.json
@@ -598,9 +598,9 @@
"send": "Send",
"sending": "Sending",
"invalid_password": "Invalid Password",
- "invalid_password_not_set": "Password is required",
- "invalid_password_too_short": "Password too short, minimum __minLength__",
- "invalid_password_too_long": "Maximum password length __maxLength__ exceeded",
+ "invalid_password_not_set": "Password is required",
+ "invalid_password_too_short": "Password too short, minimum __minLength__",
+ "invalid_password_too_long": "Maximum password length __maxLength__ exceeded",
"invalid_password_invalid_character": "Password contains an invalid character",
"invalid_password_contains_email": "Password can not contain email address",
"error": "Error",
@@ -887,6 +887,7 @@
"monthly": "Monthly",
"personal": "Personal",
"free": "Free",
+ "group_plans": "Group plans",
"one_collaborator": "Only one collaborator",
"collaborator": "Collaborator",
"collabs_per_proj": "__collabcount__ collaborators per project",