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",