diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee index 32d2abf594..03e87125ac 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee @@ -10,6 +10,7 @@ GeoIpLookup = require("../../infrastructure/GeoIpLookup") SubscriptionDomainHandler = require("./SubscriptionDomainHandler") UserGetter = require "../User/UserGetter" FeaturesUpdater = require './FeaturesUpdater' +planFeatures = require './planFeatures' module.exports = SubscriptionController = @@ -20,6 +21,7 @@ module.exports = SubscriptionController = viewName = "#{viewName}_#{req.query.v}" logger.log viewName:viewName, "showing plans page" currentUser = null + GeoIpLookup.getCurrencyCode req.query?.ip || req.ip, (err, recomendedCurrency)-> return next(err) if err? render = () -> @@ -29,6 +31,7 @@ module.exports = SubscriptionController = gaExperiments: Settings.gaExperiments.plansPage recomendedCurrency:recomendedCurrency shouldABTestPlans: currentUser == null or (currentUser?.signUpDate? and currentUser.signUpDate >= (new Date('2016-10-27'))) + planFeatures: planFeatures user_id = AuthenticationController.getLoggedInUserId(req) if user_id? UserGetter.getUser user_id, {signUpDate: 1}, (err, user) -> diff --git a/services/web/app/coffee/Features/Subscription/planFeatures.coffee b/services/web/app/coffee/Features/Subscription/planFeatures.coffee new file mode 100644 index 0000000000..8c9c276955 --- /dev/null +++ b/services/web/app/coffee/Features/Subscription/planFeatures.coffee @@ -0,0 +1,133 @@ +module.exports = + [ + { + feature: 'number_collab' + value: 'str' + plans: { + free: '1' + coll: '10' + prof: 'unlimited' + } + student: '6' + } + { + feature: 'unlimited_private' + value: 'bool' + info: 'unlimited_private_info' + plans: { + free: true + coll: true + prof: true + }, + student: true + } + { + feature: 'realtime_collab' + value: 'bool' + info: 'realtime_collab_info' + plans: { + free: true + coll: true + prof: true + } + student: true + } + { + feature: 'hundreds_templates' + value: 'bool' + info: 'hundreds_templates_info' + plans: { + free: true + coll: true + prof: true + } + student: true + } + { + feature: 'powerful_latex_editor' + value: 'bool' + info: 'latex_editor_info' + plans: { + free: true + coll: true + prof: true + } + student: true + } + { + feature: 'realtime_track_changes' + value: 'bool' + info: 'realtime_track_changes_info' + plans: { + free: false + coll: true + prof: true + }, + student: true + } + { + feature: 'reference_search' + value: 'bool' + info: 'reference_search_info' + plans: { + free: false + coll: true + prof: true + }, + student: true + }, + { + feature: 'reference_sync' + info: 'reference_sync_info' + value: 'bool' + plans: { + free: false + coll: true + prof: true + }, + student: true + } + { + feature: 'full_doc_history' + value: 'bool' + info: 'full_doc_history_info' + plans: { + free: false, + coll: true, + prof: true + }, + student: true + } + { + feature: 'dropbox_integration_lowercase' + value: 'bool' + info: 'dropbox_integration_info' + plans: { + free: false, + coll: true, + prof: true + }, + student: true + }, + { + feature: 'github_integration_lowercase' + value: 'bool' + info: 'github_integration_info' + plans: { + free: false, + coll: true, + prof: true + }, + student: true + }, + { + feature: 'priority_support', + value: 'bool', + plans: { + free: false, + coll: true, + prof: true + }, + student: true + }, + ] \ No newline at end of file diff --git a/services/web/app/views/subscriptions/_plans_page_details_less.pug b/services/web/app/views/subscriptions/_plans_page_details_less.pug new file mode 100644 index 0000000000..2aa572e4e7 --- /dev/null +++ b/services/web/app/views/subscriptions/_plans_page_details_less.pug @@ -0,0 +1,118 @@ +.row + .col-md-12 + .page-header.centered.plans-header.text-centered + h1 #{translate("start_x_day_trial", {len:'{{trial_len}}'})} +.row + .col-md-8.col-md-offset-2 + p.text-centered #{translate("sl_benefits_plans")} + +.row.top-switch + .col-md-6.col-md-offset-3 + +plan_switch('card') + .col-md-2.text-right + +currency_dropdown + +div(ng-show="showPlans") + .row + .col-md-10.col-md-offset-1 + .row + .card-group.text-centered(ng-if="ui.view == 'monthly' || ui.view == 'annual'") + .col-md-4 + .card.card-first + .card-header + h2 #{translate("personal")} + .circle #{translate("free")} + +features_free + .col-md-4 + .card.card-highlighted + .card-header + h2 #{translate("collaborator")} + .circle + +price_collaborator + +features_collaborator + .col-md-4 + .card.card-last + .card-header + h2 #{translate("professional")} + .circle + +price_professional + +features_professional + + .card-group.text-centered(ng-if="ui.view == 'student'") + .col-md-4 + .card.card-first + .card-header + h2 #{translate("personal")} + .circle #{translate("free")} + +features_free + + .col-md-4 + .card.card-highlighted + +card_student_monthly + + .col-md-4 + .card.card-last + +card_student_annual + +.row.row-spaced + p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})} + +.row + .col-md-8.col-md-offset-2 + .alert.alert-info.text-centered + | #{translate("interested_in_group_licence")} + br + a(href, ng-click="openGroupPlanModal()") #{translate("get_in_touch_for_details")} + + script(type="text/ng-template", id="groupPlanModalTemplate") + .modal-header + h3 #{translate("group_plan_enquiry")} + .modal-body + form.text-left.form(ng-controller="UniverstiesContactController", ng-submit="contactUs()") + span(ng-show="sent == false && error == false") + .form-group + label#title9(for='Field9') + | Name + input#Field9.field.text.medium.span8.form-control(ng-model="form.name", maxlength='255', tabindex='1', onkeyup='') + label#title11.desc(for='Field11') + | Email + .form-group + input#Field11.field.text.medium.span8.form-control(ng-model="form.email", name='Field11', type='email', spellcheck='false', value='', maxlength='255', tabindex='2') + label#title12.desc(for='Field12') + | University / Company + .form-group + input#Field12.field.text.medium.span8.form-control(ng-model="form.university", name='Field12', type='text', value='', maxlength='255', tabindex='3', onkeyup='') + label#title13.desc(for='Field13') + | Position + .form-group + input#Field13.field.text.medium.span8.form-control(ng-model="form.position", name='Field13', type='text', value='', maxlength='255', tabindex='4', onkeyup='') + .form-group + input(ng-model="form.source", type="hidden", ng-init="form.source = '__ref__'; form.subject = 'General enquiry for larger ShareLaTeX use';") + .form-group.text-center + input#saveForm.btn-success.btn.btn-lg(name='saveForm', type='submit', ng-disabled="sending", value='Request a quote') + span(ng-show="sent == true && error == false") + p Request Sent, Thank you. + span(ng-show="error") + p Error sending request. + +.row + .col-md-12 + .page-header.plans-header.plans-subheader.text-centered + h2 #{translate("enjoy_these_features")} + .col-md-4 + .card.features.text-centered + i.fa.fa-file-text-o.fa-5x + h4 #{translate("unlimited_projects")} + p #{translate("create_unlimited_projects")} + .col-md-4 + .card.features.text-centered + i.fa.fa-clock-o.fa-5x + h4 #{translate("full_doc_history")} + p #{translate("never_loose_work")} + .col-md-4 + .card.features.text-centered + i.fa.fa-dropbox.fa-5x + |     + i.fa.fa-github.fa-5x + h4 #{translate("sync_to_dropbox_and_github")} + p #{translate("access_projects_anywhere")} \ No newline at end of file diff --git a/services/web/app/views/subscriptions/_plans_page_details_more.pug b/services/web/app/views/subscriptions/_plans_page_details_more.pug new file mode 100644 index 0000000000..17c683201e --- /dev/null +++ b/services/web/app/views/subscriptions/_plans_page_details_more.pug @@ -0,0 +1,160 @@ +.row + .col-md-12 + .page-header.centered.plans-header.text-centered + h1.text-capitalize #{translate('instant_access')} +.row + .col-md-8.col-md-offset-2 + p.text-centered #{translate("sl_benefits_plans")} + +.row.top-switch + .col-md-6.col-md-offset-3 + +plan_switch('card') + .col-md-2.text-right + +currency_dropdown + +div(ng-show="showPlans") + .row + .col-md-10.col-md-offset-1 + .row + .card-group.text-centered(ng-if="ui.view == 'monthly' || ui.view == 'annual'") + .col-md-4 + .card.card-first + .card-header + h2 #{translate("personal")} + h5.tagline #{translate("tagline_personal")} + .circle #{translate("free")} + +features_free + .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 + +features_collaborator + .col-md-4 + .card.card-last + .card-header + h2 #{translate("professional")} + h5.tagline #{translate("tagline_professional")} + .circle + +price_professional + +features_professional + + .card-group.text-centered(ng-if="ui.view == 'student'") + .col-md-4 + .card.card-first + .card-header + h2 #{translate("personal")} + h5.tagline #{translate("tagline_personal")} + .circle #{translate("free")} + +features_free + + .col-md-4 + .card.card-highlighted + +card_student_annual + + .col-md-4 + .card.card-last + +card_student_monthly + +.row.row-spaced-large.text-centered + i.fa.fa-cc-mastercard.fa-2x   + i.fa.fa-cc-visa.fa-2x   + i.fa.fa-cc-amex.fa-2x   + i.fa.fa-cc-paypal.fa-2x   + div.text-centered #{translate('change_plans_any_time')}
#{translate('billed_after_x_days', {len:'{{trial_len}}'})} + +.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-info(href="/i/university/groups") #{translate('find_out_more')} + +div + .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") + .col-sm-12(ng-if="ui.view != 'student'") + +table_premium + .col-sm-12(ng-if="ui.view == 'student'") + +table_student + + .row.row-spaced-large + .col-md-12 + .page-header.plans-header.plans-subheader.text-centered + h2 #{translate('in_good_company')} + .row + .col-md-6 + div + .row + .col-md-3 + .circle-img + img(src=buildImgPath('advocates/erdogmus.jpg') alt="Professor Erdogmus") + .col-md-9 + blockquote + p The ability to track changes and the real-time collaborative nature is what sets ShareLaTeX apart. + footer Professor Erdogmus, Northeastern University + .col-md-6 + div + .row + .col-md-3 + .circle-img + img(src=buildImgPath('advocates/henderson.jpg') alt="Rob Henderson") + .col-md-9 + blockquote + p ShareLaTeX has proven to be a powerful and robust collaboration tool that is widely used in our School. + footer Rob Henderson, School Of Informatics And Computing - Indiana University + + .faq + .row.row-spaced-large + .col-md-12 + .page-header.plans-header.plans-subheader.text-centered + h2 FAQ + .row + .col-md-6 + h3 #{translate("faq_how_free_trial_works_question")} + p #{translate('faq_how_free_trial_works_answer', { len:'{{trial_len}}' })} + .col-md-6 + h3 #{translate('faq_change_plans_question')} + p #{translate('faq_change_plans_answer')} + .row + .col-md-6 + h3 #{translate('faq_do_collab_need_premium_question')} + p #{translate('faq_do_collab_need_premium_answer')} + .col-md-6 + h3 #{translate('faq_need_more_collab_question')} + p !{translate('faq_need_more_collab_answer', { referFriendsLink: '' + translate('referring_your_friends') + ''})} + .row + .col-md-6 + h3 #{translate('faq_purchase_more_licenses_question')} + p !{translate('faq_purchase_more_licenses_answer', { groupLink: '' + translate('discounted_group_accounts') + '' })} + .col-md-6 + h3 #{translate('faq_monthly_or_annual_question')} + p #{translate('faq_monthly_or_annual_answer')} + .row + .col-md-6 + h3 #{translate('faq_how_to_pay_question')} + p #{translate('faq_how_to_pay_answer')} + .col-md-6 + h3 #{translate('faq_pay_by_invoice_question')} + p !{translate('faq_pay_by_invoice_answer', { groupLink: '' + translate('discounted_group_accounts') + '' })} + .row.row-spaced-large.text-centery + .col-md-12 + .plans-header.plans-subheader.text-centered + h2 #{translate('still_have_questions')} + button.btn.btn-info.btn-header.text-capitalize(ng-controller="ContactGeneralModal" ng-click="openModal()") #{translate('get_in_touch')} + != moduleIncludes("contactModalGeneral", locals) diff --git a/services/web/app/views/subscriptions/_plans_page_mixins.pug b/services/web/app/views/subscriptions/_plans_page_mixins.pug new file mode 100644 index 0000000000..c1f19bca00 --- /dev/null +++ b/services/web/app/views/subscriptions/_plans_page_mixins.pug @@ -0,0 +1,162 @@ +//- Buy Buttons +mixin btn_buy_collaborator(location) + a.btn.btn-info( + ng-href="/user/subscription/new?planCode={{ getCollaboratorPlanCode() }}¤cy={{currencyCode}}", + ng-click="signUpNowClicked('collaborator','" + location + "')" + ) + span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")} + span(ng-show="ui.view == 'annual'") #{translate("buy_now")} +mixin btn_buy_free(location) + a.btn.btn-info( + href="/register" + style=(getLoggedInUserId() === null ? "" : "visibility: hidden") + ng-click="signUpNowClicked('free','" + location + "')" + ) + span(ng-if="plansVariant !== 'more-details'") #{translate('sign_up_now')} + span.text-capitalize(ng-if="plansVariant === 'more-details'") #{translate('get_started_now')} +mixin btn_buy_professional(location) + a.btn.btn-info( + ng-href="/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || planQueryString}}¤cy={{currencyCode}}" + ng-click="signUpNowClicked('professional','" + location + "')" + ) + span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")} + span(ng-show="ui.view == 'annual'") #{translate("buy_now")} +mixin btn_buy_student(location, plan) + if plan == 'annual' + a.btn.btn-info( + ng-href="/user/subscription/new?planCode=student-annual¤cy={{currencyCode}}", + ng-click="signUpNowClicked('student-annual','" + location + "')" + ) #{translate("buy_now")} + else + //- planQueryString will contain _free_trial_7_days + a.btn.btn-info( + ng-href="/user/subscription/new?planCode=student{{planQueryString}}¤cy={{currencyCode}}", + ng-click="signUpNowClicked('student-monthly','" + location + "')" + ) #{translate("start_free_trial")} + +//- Cards +mixin card_student_annual + .best-value(ng-if="plansVariant == 'more-details'") + strong #{translate('best_value')} + .card-header + h2 #{translate("student")} (#{translate("annual")}) + h5.tagline(ng-if="plansVariant == 'more-details'") #{translate('tagline_student_annual')} + .circle + span + +price_student_annual + +features_student('card', 'annual') +mixin card_student_monthly + .card-header + h2 #{translate("student")} + h5.tagline(ng-if="plansVariant == 'more-details'") #{translate('tagline_student_monthly')} + .circle + span + +price_student_monthly + +features_student('card', 'monthly') + +//- Features Lists +mixin features_collaborator + ul.list-unstyled + li + strong #{translate("collabs_per_proj", {collabcount:10})} + +features_premium + li + br + +btn_buy_collaborator('card') +mixin features_free + ul.list-unstyled + li #{translate("one_collaborator")} + li(class="hidden-xs hidden-sm")   + li(class="hidden-xs hidden-sm")   + li(class="hidden-xs hidden-sm")   + li(class="hidden-xs hidden-sm" ng-if="plansVariant === 'more-details'")   + li(class="hidden-xs hidden-sm" ng-if="plansVariant === 'more-details'")   + li(class="hidden-xs hidden-sm" ng-if="plansVariant === 'more-details'")   + li + br + +btn_buy_free('card') +mixin features_premium + li(ng-if="plansVariant != 'more-details'") #{translate("full_doc_history")} + li(ng-if="plansVariant != 'more-details'") #{translate("sync_to_dropbox")} + li(ng-if="plansVariant != 'more-details'") #{translate("sync_to_github")} + li(ng-if="plansVariant === 'more-details'")   + li(ng-if="plansVariant === 'more-details'") + strong #{translate('all_premium_features')} + li(ng-if="plansVariant === 'more-details'") #{translate('sync_dropbox_github')} + li(ng-if="plansVariant === 'more-details'") #{translate('full_doc_history')} + li(ng-if="plansVariant === 'more-details'") #{translate('track_changes')} + li(ng-if="plansVariant === 'more-details'") + #{translate('more').toLowerCase()} +mixin features_professional + ul.list-unstyled + li + strong #{translate("unlimited_collabs")} + +features_premium + li + br + +btn_buy_professional('card') +mixin features_student(location, plan) + ul.list-unstyled + li + strong #{translate("collabs_per_proj", {collabcount:6})} + +features_premium + li + br + +btn_buy_student(location, plan) + +//- Prices +mixin price_collaborator + span(ng-if="ui.view == 'monthly'") + | {{plans[currencyCode]['collaborator']['monthly']}} + span.small /mo + span(ng-if="ui.view == 'annual'") + | {{plans[currencyCode]['collaborator']['annual']}} + span.small /yr +mixin price_professional + span(ng-if="ui.view == 'monthly'") + | {{plans[currencyCode]['professional']['monthly']}} + span.small /mo + span(ng-if="ui.view == 'annual'") + | {{plans[currencyCode]['professional']['annual']}} + span.small /yr +mixin price_student_annual + | {{plans[currencyCode]['student']['annual']}} + span.small /yr +mixin price_student_monthly + | {{plans[currencyCode]['student']['monthly']}} + span.small /mo + +//- UI Control +mixin currency_dropdown + .dropdown.currency-dropdown(dropdown) + a.btn.btn-default.dropdown-toggle( + href="#", + data-toggle="dropdown", + dropdown-toggle + ) + | {{currencyCode}} ({{plans[currencyCode]['symbol']}}) + span.caret + + ul.dropdown-menu.dropdown-menu-right.text-right(role="menu") + li(ng-repeat="(currency, value) in plans") + a( + href="#", + ng-click="changeCurreny($event, currency)" + ) {{currency}} ({{value['symbol']}}) +mixin plan_switch(location) + ul.nav.nav-pills + li(ng-class="{'active': ui.view == 'monthly'}") + a( + href="#" + ng-click="switchToMonthly($event,'" + location + "')" + ) #{translate("monthly")} + li(ng-class="{'active': ui.view == 'annual'}") + a( + href="#" + ng-click="switchToAnnual($event,'" + location + "')" + ) #{translate("annual")} + li(ng-class="{'active': ui.view == 'student'}") + a( + href="#" + ng-click="switchToStudent($event,'" + location + "')" + ) #{translate("half_price_student")} + diff --git a/services/web/app/views/subscriptions/_plans_page_tables.pug b/services/web/app/views/subscriptions/_plans_page_tables.pug new file mode 100644 index 0000000000..63c4747603 --- /dev/null +++ b/services/web/app/views/subscriptions/_plans_page_tables.pug @@ -0,0 +1,107 @@ + +//- Features Tables +mixin table_premium + table.card.plans-table + tr + th + th #{translate("personal")} + th #{translate("collaborator")} + .outer.outer-top + .outer-content + .best-value + strong #{translate('best_value')} + th #{translate("professional")} + + tr + td #{translate("price")} + td #{translate("free")} + td + +price_collaborator + td + +price_professional + + for feature in planFeatures + tr + td(event-tracking="features-table" event-tracking-trigger="hover" event-tracking-ga="subscription-funnel" event-tracking-label=`${feature.feature}-exp-{{plansVariant}}`) + if feature.info + span(tooltip=translate(feature.info)) #{translate(feature.feature)} + else + | #{translate(feature.feature)} + for plan in feature.plans + td + if feature.value == 'str' + | #{plan} + else if plan + i.fa.fa-check + else + i.fa.fa-times + + tr + td + td + +btn_buy_free('table') + td + +btn_buy_collaborator('table') + .outer.outer-btm + .outer-content   + td + +btn_buy_professional('table') + +mixin table_cell_student(feature) + if feature.value == 'str' + | #{feature.student} + else if feature.student + i.fa.fa-check + else + i.fa.fa-times + +mixin table_student + table.card.plans-table + tr + th + th #{translate("personal")} + th #{translate("student")} (#{translate("annual")}) + .outer.outer-top + .outer-content + .best-value + strong Best Value + th #{translate("student")} + + tr + td #{translate("price")} + td #{translate("free")} + td + +price_student_annual + td + +price_student_monthly + + for feature in planFeatures + tr + td(event-tracking="plans-page-table" event-tracking-trigger="hover" event-tracking-ga="subscription-funnel" event-tracking-label=`${feature.feature}-exp-{{plansVariant}}`) + if feature.info + span(tooltip=translate(feature.info)) #{translate(feature.feature)} + else + | #{translate(feature.feature)} + td + if feature.value == 'str' + | #{feature.plans.free} + else if feature.plans.free + i.fa.fa-check + else + i.fa.fa-times + td + +table_cell_student(feature) + td + +table_cell_student(feature) + + tr + td + td + +btn_buy_free('table') + td + +btn_buy_student('table', 'annual') + .outer.outer-btm + .outer-content   + td + +btn_buy_student('table', 'monthly') + diff --git a/services/web/app/views/subscriptions/new.pug b/services/web/app/views/subscriptions/new.pug index d86bdb8166..4eae442f1e 100644 --- a/services/web/app/views/subscriptions/new.pug +++ b/services/web/app/views/subscriptions/new.pug @@ -31,7 +31,10 @@ block content li(ng-repeat="(currency, value) in plans") a( ng-click="changeCurrency(currency)", - ) {{currency}} ({{value['symbol']}}) + ) {{currency}} ({{value['symbol']}}) + .row(ng-if="plansVariant == 'more-details' && planCode == 'student-annual' || plansVariant == 'more-details' && planCode == 'student-monthly'") + .col-xs-12 + p.student-disclaimer #{translate('student_disclaimer')} hr.thin .row .col-md-12.text-center diff --git a/services/web/app/views/subscriptions/plans.pug b/services/web/app/views/subscriptions/plans.pug index 7c40c38a52..56a20f90f5 100644 --- a/services/web/app/views/subscriptions/plans.pug +++ b/services/web/app/views/subscriptions/plans.pug @@ -1,253 +1,18 @@ extends ../layout + +include _plans_page_mixins +include _plans_page_tables + block scripts script(type='text/javascript'). window.recomendedCurrency = '#{recomendedCurrency}' window.abCurrencyFlag = '#{abCurrencyFlag}' window.shouldABTestPlans = #{shouldABTestPlans || false} - script(type='text/javascript'). - (function() {var s=document.createElement('script'); s.type='text/javascript';s.async=true; - s.src=('https:'==document.location.protocol?'https':'http') + '://sharelatex-accounts.groovehq.com/widgets/f5ad3b09-7d99-431b-8af5-c5725e3760ce/ticket/api.js'; - var q = document.getElementsByTagName('script')[0];q.parentNode.insertBefore(s, q);})(); - block content .content.content-alt .content.plans(ng-controller="PlansController") - .container - .row - .col-md-12 - .page-header.centered.plans-header.text-centered - h1(ng-cloak) #{translate("start_x_day_trial", {len:'{{trial_len}}'})} - .row - .col-md-8.col-md-offset-2 - p.text-centered #{translate("sl_benefits_plans")} - - .row(ng-cloak) - .col-md-6.col-md-offset-3 - ul.nav.nav-pills - li(ng-class="{'active': ui.view == 'monthly'}") - a( - href, - ng-click="switchToMonthly()" - ) #{translate("monthly")} - li(ng-class="{'active': ui.view == 'annual'}") - a( - href - ng-click="switchToAnnual()" - ) #{translate("annual")} - li(ng-class="{'active': ui.view == 'student'}") - a( - href, - ng-click="switchToStudent()" - ) #{translate("half_price_student")} - .col-md-2.text-right - .dropdown.currency-dropdown(dropdown) - a.btn.btn-default.dropdown-toggle#currenyDropdown( - href="#", - data-toggle="dropdown", - dropdown-toggle - ) - | {{currencyCode}} ({{plans[currencyCode]['symbol']}}) - span.caret - - ul.dropdown-menu.dropdown-menu-right.text-right(role="menu") - li(ng-repeat="(currency, value) in plans") - a( - href, - ng-click="changeCurreny(currency)" - ) {{currency}} ({{value['symbol']}}) - - div(ng-show="showPlans") - .row(ng-cloak) - .col-md-10.col-md-offset-1 - .row - .card-group.text-centered(ng-if="ui.view == 'monthly' || ui.view == 'annual'") - .col-md-4 - .card.card-first - .card-header - h2 #{translate("personal")} - .circle #{translate("free")} - ul.list-unstyled - li #{translate("one_collaborator")} - li   - li   - li   - li - br - a.btn.btn-info( - href="/register" - style=(getLoggedInUserId() === null ? "" : "visibility: hidden") - ) #{translate("sign_up_now")} - .col-md-4 - .card.card-highlighted - .card-header - h2 #{translate("collaborator")} - .circle - span(ng-if="ui.view == 'monthly'") - | {{plans[currencyCode]['collaborator']['monthly']}} - span.small /mo - span(ng-if="ui.view == 'annual'") - | {{plans[currencyCode]['collaborator']['annual']}} - span.small /yr - ul.list-unstyled - li - strong #{translate("collabs_per_proj", {collabcount:10})} - li #{translate("full_doc_history")} - li #{translate("sync_to_dropbox")} - li #{translate("sync_to_github")} - li - br - a.btn.btn-info( - ng-href="/user/subscription/new?planCode={{ getCollaboratorPlanCode() }}¤cy={{currencyCode}}", ng-click="signUpNowClicked('collaborator')" - ) - span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")} - span(ng-show="ui.view == 'annual'") #{translate("buy_now")} - .col-md-4 - .card.card-last - .card-header - h2 #{translate("professional")} - .circle - span(ng-if="ui.view == 'monthly'") - | {{plans[currencyCode]['professional']['monthly']}} - span.small /mo - span(ng-if="ui.view == 'annual'") - | {{plans[currencyCode]['professional']['annual']}} - span.small /yr - ul.list-unstyled - li - strong #{translate("unlimited_collabs")} - li #{translate("full_doc_history")} - li #{translate("sync_to_dropbox")} - li #{translate("sync_to_github")} - li - br - a.btn.btn-info( - ng-href="/user/subscription/new?planCode=professional{{ ui.view == 'annual' && '-annual' || planQueryString}}¤cy={{currencyCode}}", ng-click="signUpNowClicked('professional')" - ) - span(ng-show="ui.view != 'annual'") #{translate("start_free_trial")} - span(ng-show="ui.view == 'annual'") #{translate("buy_now")} - - .card-group.text-centered(ng-if="ui.view == 'student'") - .col-md-4 - .card.card-first - .card-header - h2 #{translate("personal")} - .circle #{translate("free")} - ul.list-unstyled - li #{translate("one_collaborator")} - li   - li   - li   - li - br - a.btn.btn-info( - href="/register" - style=(getLoggedInUserId() === null ? "" : "visibility: hidden") - ) #{translate("sign_up_now")} - - .col-md-4 - .card.card-highlighted - .card-header - h2 #{translate("student")} - .circle - span - | {{plans[currencyCode]['student']['monthly']}} - span.small /mo - ul.list-unstyled - li - strong #{translate("collabs_per_proj", {collabcount:6})} - li #{translate("full_doc_history")} - li #{translate("sync_to_dropbox")} - li #{translate("sync_to_github")} - li - br - a.btn.btn-info( - ng-href="/user/subscription/new?planCode=student{{ plansVariant == 'default' ? planQueryString : '_'+plansVariant }}¤cy={{currencyCode}}", - ng-click="signUpNowClicked('student-monthly')" - ) #{translate("start_free_trial")} - - .col-md-4 - .card.card-last - .card-header - h2 #{translate("student")} (#{translate("annual")}) - .circle - span - | {{plans[currencyCode]['student']['annual']}} - span.small /yr - ul.list-unstyled - li - strong #{translate("collabs_per_proj", {collabcount:6})} - li #{translate("full_doc_history")} - li #{translate("sync_to_dropbox")} - li #{translate("sync_to_github")} - li - br - a.btn.btn-info( - ng-href="/user/subscription/new?planCode=student-annual{{ plansVariant == 'default' ? '' : '_'+plansVariant }}¤cy={{currencyCode}}", - ng-click="signUpNowClicked('student-annual')" - ) #{translate("buy_now")} - - - - .row.row-spaced(ng-cloak) - p.text-centered #{translate("choose_plan_works_for_you", {len:'{{trial_len}}'})} - - .row(ng-cloak) - .col-md-8.col-md-offset-2 - .alert.alert-info.text-centered - | #{translate("interested_in_group_licence")} - br - a(href, ng-click="openGroupPlanModal()") #{translate("get_in_touch_for_details")} - - script(type="text/ng-template", id="groupPlanModalTemplate") - .modal-header - h3 #{translate("group_plan_enquiry")} - .modal-body - form.text-left.form(ng-controller="UniverstiesContactController", ng-submit="contactUs()", ng-cloak) - span(ng-show="sent == false && error == false") - .form-group - label#title9(for='Field9') - | Name - input#Field9.field.text.medium.span8.form-control(ng-model="form.name", maxlength='255', tabindex='1', onkeyup='') - label#title11.desc(for='Field11') - | Email - .form-group - input#Field11.field.text.medium.span8.form-control(ng-model="form.email", name='Field11', type='email', spellcheck='false', value='', maxlength='255', tabindex='2') - label#title12.desc(for='Field12') - | University / Company - .form-group - input#Field12.field.text.medium.span8.form-control(ng-model="form.university", name='Field12', type='text', value='', maxlength='255', tabindex='3', onkeyup='') - label#title13.desc(for='Field13') - | Position - .form-group - input#Field13.field.text.medium.span8.form-control(ng-model="form.position", name='Field13', type='text', value='', maxlength='255', tabindex='4', onkeyup='') - .form-group - input(ng-model="form.source", type="hidden", ng-init="form.source = '__ref__'; form.subject = 'General enquiry for larger ShareLaTeX use';") - .form-group.text-center - input#saveForm.btn-success.btn.btn-lg(name='saveForm', type='submit', ng-disabled="sending", value='Request a quote') - span(ng-show="sent == true && error == false") - p Request Sent, Thank you. - span(ng-show="error") - p Error sending request. - - .row - .col-md-12 - .page-header.plans-header.plans-subheader.text-centered - h2 #{translate("enjoy_these_features")} - .col-md-4 - .card.features.text-centered - i.fa.fa-file-text-o.fa-5x - h4 #{translate("unlimited_projects")} - p #{translate("create_unlimited_projects")} - .col-md-4 - .card.features.text-centered - i.fa.fa-clock-o.fa-5x - h4 #{translate("full_doc_history")} - p #{translate("never_loose_work")} - .col-md-4 - .card.features.text-centered - i.fa.fa-dropbox.fa-5x - |     - i.fa.fa-github.fa-5x - h4 #{translate("sync_to_dropbox_and_github")} - p #{translate("access_projects_anywhere")} + .container(class="more-details" ng-cloak ng-if="plansVariant === 'more-details'") + include _plans_page_details_more + .container(ng-cloak ng-if="plansVariant != 'more-details'") + include _plans_page_details_less diff --git a/services/web/public/coffee/main/event.coffee b/services/web/public/coffee/main/event.coffee index 3ff8fc0aa4..22b7cbb4b6 100644 --- a/services/web/public/coffee/main/event.coffee +++ b/services/web/public/coffee/main/event.coffee @@ -93,4 +93,4 @@ define [ $('.navbar a').on "click", (e)-> href = $(e.target).attr("href") if href? - ga('send', 'event', 'navigation', 'top menu bar', href) + ga('send', 'event', 'navigation', 'top menu bar', href) \ No newline at end of file diff --git a/services/web/public/coffee/main/new-subscription.coffee b/services/web/public/coffee/main/new-subscription.coffee index 31d8e37f40..7851171524 100644 --- a/services/web/public/coffee/main/new-subscription.coffee +++ b/services/web/public/coffee/main/new-subscription.coffee @@ -9,6 +9,7 @@ define [ $scope.currencyCode = MultiCurrencyPricing.currencyCode $scope.plans = MultiCurrencyPricing.plans + $scope.planCode = window.plan_code $scope.switchToStudent = ()-> currentPlanCode = window.plan_code @@ -234,3 +235,6 @@ define [ {code:'WK',name:'Wake Island'},{code:'WF',name:'Wallis and Futuna'},{code:'EH',name:'Western Sahara'},{code:'YE',name:'Yemen'}, {code:'ZM',name:'Zambia'},{code:'AX',name:'Åland Islandscode:'} ] + + sixpack.participate 'plans', ['default', 'more-details'], (chosenVariation, rawResponse)-> + $scope.plansVariant = chosenVariation \ No newline at end of file diff --git a/services/web/public/coffee/main/plans.coffee b/services/web/public/coffee/main/plans.coffee index 9a62420d66..7eb63607c0 100644 --- a/services/web/public/coffee/main/plans.coffee +++ b/services/web/public/coffee/main/plans.coffee @@ -3,7 +3,6 @@ define [ "libs/recurly-4.8.5" ], (App, recurly) -> - App.factory "MultiCurrencyPricing", () -> currencyCode = window.recomendedCurrency @@ -146,17 +145,16 @@ define [ } - App.controller "PlansController", ($scope, $modal, event_tracking, abTestManager, MultiCurrencyPricing, $http, sixpack) -> + App.controller "PlansController", ($scope, $modal, event_tracking, abTestManager, MultiCurrencyPricing, $http, sixpack, $filter) -> $scope.showPlans = false - - $scope.plansVariant = 'default' $scope.shouldABTestPlans = window.shouldABTestPlans if $scope.shouldABTestPlans - $scope.showPlans = true - else - $scope.showPlans = true + sixpack.participate 'plans-details', ['default', 'more-details'], (chosenVariation, rawResponse)-> + $scope.plansVariant = chosenVariation + + $scope.showPlans = true $scope.plans = MultiCurrencyPricing.plans @@ -169,44 +167,57 @@ define [ $scope.ui = view: "monthly" - $scope.changeCurreny = (newCurrency)-> + $scope.changeCurreny = (e, newCurrency)-> + e.preventDefault() $scope.currencyCode = newCurrency # because ternary logic in angular bindings is hard $scope.getCollaboratorPlanCode = () -> view = $scope.ui.view - variant = $scope.plansVariant if view == "annual" - if variant == "default" - return "collaborator-annual" - else - return "collaborator-annual_#{variant}" + return "collaborator-annual" else - if variant == "default" - return "collaborator#{$scope.planQueryString}" - else - return "collaborator_#{variant}" + return "collaborator#{$scope.planQueryString}" - $scope.signUpNowClicked = (plan, annual)-> - event_tracking.sendMB 'plans-page-start-trial', {plan} + $scope.signUpNowClicked = (plan, location)-> if $scope.ui.view == "annual" plan = "#{plan}_annual" - event_tracking.send 'subscription-funnel', 'sign_up_now_button', plan + plan = eventLabel(plan, location) + event_tracking.sendMB 'plans-page-start-trial', {plan} + event_tracking.send 'subscription-funnel', 'sign_up_now_button', plan + if $scope.shouldABTestPlans + sixpack.convert 'plans-details' - $scope.switchToMonthly = -> - $scope.ui.view = "monthly" - event_tracking.send 'subscription-funnel', 'plans-page', 'monthly-prices' + $scope.switchToMonthly = (e, location) -> + uiView = 'monthly' + switchEvent(e, uiView + '-prices', location) + $scope.ui.view = uiView - $scope.switchToStudent = -> - $scope.ui.view = "student" - event_tracking.send 'subscription-funnel', 'plans-page', 'student-prices' + $scope.switchToStudent = (e, location) -> + uiView = 'student' + switchEvent(e, uiView + '-prices', location) + $scope.ui.view = uiView - $scope.switchToAnnual = -> - $scope.ui.view = "annual" - event_tracking.send 'subscription-funnel', 'plans-page', 'annual-prices' + $scope.switchToAnnual = (e, location) -> + uiView = 'annual' + switchEvent(e, uiView + '-prices', location) + $scope.ui.view = uiView $scope.openGroupPlanModal = () -> $modal.open { templateUrl: "groupPlanModalTemplate" } event_tracking.send 'subscription-funnel', 'plans-page', 'group-inquiry-potential' + + eventLabel = (label, location) -> + if location && $scope.plansVariant != 'default' + label = label + '-' + location + if $scope.plansVariant != 'default' + label += '-exp-' + $scope.plansVariant + label + + switchEvent = (e, label, location) -> + e.preventDefault() + gaLabel = eventLabel(label, location) + event_tracking.send 'subscription-funnel', 'plans-page', gaLabel + diff --git a/services/web/public/img/advocates/erdogmus.jpg b/services/web/public/img/advocates/erdogmus.jpg new file mode 100644 index 0000000000..e70a641fd0 Binary files /dev/null and b/services/web/public/img/advocates/erdogmus.jpg differ diff --git a/services/web/public/img/advocates/henderson.jpg b/services/web/public/img/advocates/henderson.jpg new file mode 100644 index 0000000000..549570955c Binary files /dev/null and b/services/web/public/img/advocates/henderson.jpg differ diff --git a/services/web/public/stylesheets/app/plans.less b/services/web/public/stylesheets/app/plans.less index b830db9b23..41e2fc80f1 100644 --- a/services/web/public/stylesheets/app/plans.less +++ b/services/web/public/stylesheets/app/plans.less @@ -32,7 +32,7 @@ padding-bottom: @line-height-computed * 2; } } - + .circle { font-size: 1.5rem; font-weight: 700; @@ -63,6 +63,12 @@ } .card .btn { white-space:normal; } + + .top-switch { + .currency-dropdown { + margin-right: -15px; + } + } } #changePlanSection { @@ -127,4 +133,261 @@ input.paymentTypeOption.ng-valid { text-align: right; } +/** + Plans Test +*/ +@best-val-height: 35px; +@highlight-border: 3px; +@highlight-color: #d3584b; +@gray-med: #6d6d6d; +@white-med: #fdfdfd; +.more-details { + .best-value { + color: @red; + line-height: @line-height-computed; + } + blockquote { + footer{ + /* accessibility fix */ + color: @gray-med; + } + } + .btn-header { + font-family: @font-family-sans-serif; + margin-left: 10px; + margin-top: -10px; + text-shadow: 0 0 0; + } + .card-first, .card-last { + background: @white-med; + } + .card-highlighted { + border: @highlight-border solid @gray-lighter; + padding-top: 10px!important; + .best-value { + margin-bottom: 15px; + } + .card-header { + padding-bottom: 22px; /* align hr with other plans */ + } + } + .card-header { + margin-bottom: 15px; + } + .circle { + /* accessibility fix */ + span.small { + color: rgba(255, 255, 255, 0.85) + } + } + .circle-img { + border-radius: 50%; + float: right; + height: 100px; + overflow: hidden; + position: relative; + width: 100px; + img { + display: inline; + margin: 0 auto; + width: 100%; + } + } + .faq:last-child { + p { + margin-bottom: 0; + } + } + .questions-header { + color: @red; + line-height: 37px; + margin: 0; + text-align: right; + } + .tagline { + margin-bottom: 20px; + } + /* Media Queries */ + @media (max-width: @screen-md-min) { + .card-highlighted { + /*override style in cards.less */ + margin-top: @line-height-computed!important; + } + .circle-img { + float: left; + margin: 0 15px; + } + } + @media (min-width: @screen-md-min) { + blockquote { + margin-bottom: 0; + } + .faq { + .row:nth-child(2) { + h3 { + margin-top: 0; + } + } + } + } +} +.student-disclaimer { + font-size: 14px; /* match .paymentPageFeatures p */ + color: @gray; /* match .paymentPageFeatures p */ + margin: 12.5px 0 0 0; +} + +/** + Plans Table +*/ +.plans-table { + border: 1px solid @gray-lighter; + background-color: @white-med; + margin: @best-val-height 0 15px 0; + table-layout: fixed; + width: 100%; + + th, td { + -moz-background-clip: padding; + -webkit-background-clip: padding; + background-clip: padding-box; /* needed for firefox when there is bg color */ + border: 1px solid @gray-lighter; + padding: 6px; + text-align: center; + vertical-align: middle; + } + + td { + font-weight: bold; + } + + th { + border-top: 0; + font-family: @headings-font-family; + font-size: @font-size-h2; + font-weight: @headings-font-weight; + line-height: @headings-line-height; + padding: 18px; + } + + th:first-child, td:first-child { + border-left: 0; + } + + th:last-child, td:last-child { + border-right: 0; + } + + td:first-child { + font-weight: bold; + padding-left: 18px; + text-align: left; + } + + tr:first-child { + th { + position: relative; + /* keep here position here, otherwise messes up border on safari */ + } + } + + tr:last-child { + td { + border-bottom: 0; + padding: 18px; + } + /* highlighted column */ + td:nth-child(3) { + position: relative; + /* keep here position here, otherwise messes up border on safari when there is a bg color */ + &:before { + /* needed for safafi */ + border-top: 1px solid @gray-lighter; + content: ''; + left: 0; + position: absolute; + top: -1px; + width: 100%; + } + } + td:first-child { + border: 0; + } + } + + .fa-check { + color: @green; + } + + /* accessibility fixes */ + .small { + color: @gray-med; + } + + /* highlighted column */ + td:nth-child(3), th:nth-child(3) { + background-color: white; + border-left: @highlight-border solid @gray-lighter; + border-right: @highlight-border solid @gray-lighter; + } + .outer { + left: -@highlight-border; + right: -@highlight-border; + position: absolute; + + .outer-content { + background: white; + border: @highlight-border solid @gray-lighter; + border-radius: @border-radius-base; + font-size: @font-size-base; + font-family: @font-family-sans-serif; + font-weight: bold; + height: @best-val-height; + padding-top: 10px; + } + } + .outer.outer-top { + top: -@best-val-height; + .outer-content { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom: 0; + } + } + .outer.outer-btm { + bottom: -@best-val-height/2; + .outer-content { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: 0; + height: @best-val-height/2; + } + } + + /* highlight rows on hover */ + tr:hover { + td { + background-color: @gray-lightest; + } + } + tr:first-child:hover { + background-color: transparent; + } + tr:last-child:hover { + background-color: transparent; + td { + background-color: transparent; + } + } + + /* tooltip */ + sup { + color: @red; + cursor: pointer; + margin-left: 5px; + } + .tooltip.in { + min-width: 200px + } +} diff --git a/services/web/public/stylesheets/core/scaffolding.less b/services/web/public/stylesheets/core/scaffolding.less index df52ab1fb4..abadcabae9 100755 --- a/services/web/public/stylesheets/core/scaffolding.less +++ b/services/web/public/stylesheets/core/scaffolding.less @@ -161,4 +161,7 @@ hr { margin-top: @line-height-computed / 2; } +.row-spaced-large { + margin-top: @line-height-computed * 2; +} diff --git a/services/web/public/stylesheets/core/type.less b/services/web/public/stylesheets/core/type.less index ac1dcf3765..c8f2dcb9f1 100755 --- a/services/web/public/stylesheets/core/type.less +++ b/services/web/public/stylesheets/core/type.less @@ -122,6 +122,11 @@ cite { font-style: normal; } text-align: center; } +// Transformations +.text-capitalize { + text-transform: capitalize; +} + // Contextual backgrounds // For now we'll leave these alongside the text classes until v4 when we can // safely shift things around (per SemVer rules). @@ -256,7 +261,14 @@ blockquote { vertical-align: -0.4em; line-height: 0.1em; } - + + &:after { + content: close-quote; + display: inherit; + height: 0; + visibility: hidden; + } + p { display: inline; }