Просмотр исходного кода

Add plans variant

Also, fix for multiple quotes. Without a closing quote subsequent quotes are considered nested, and
will use a ‘ instead of “

Also, move repeated elements to partials
Jessica Lawshe 8 лет назад
Родитель
Сommit
10cf5825a5

+ 3 - 0
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) ->

+ 133 - 0
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
+		},
+	]

+ 118 - 0
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")}

+ 160 - 0
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')}<br/> #{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: '<a href="/user/bonus">' + translate('referring_your_friends') + '</a>'})}
+		.row
+			.col-md-6
+				h3 #{translate('faq_purchase_more_licenses_question')}
+				p !{translate('faq_purchase_more_licenses_answer', { groupLink: '<a href="/i/university/groups">' + translate('discounted_group_accounts') + '</a>' })}
+			.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: '<a href="/i/university/groups">' + translate('discounted_group_accounts') + '</a>' })}
+		.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)

+ 162 - 0
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() }}&currency={{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}}&currency={{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&currency={{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}}&currency={{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") &nbsp;
+		li(class="hidden-xs hidden-sm") &nbsp;
+		li(class="hidden-xs hidden-sm") &nbsp;
+		li(class="hidden-xs hidden-sm" ng-if="plansVariant === 'more-details'") &nbsp;
+		li(class="hidden-xs hidden-sm" ng-if="plansVariant === 'more-details'") &nbsp;
+		li(class="hidden-xs hidden-sm" ng-if="plansVariant === 'more-details'") &nbsp;
+		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'") &nbsp;
+	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")}
+

+ 107 - 0
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 &nbsp;
+			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 &nbsp;
+			td
+				+btn_buy_student('table', 'monthly')
+

+ 4 - 1
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

+ 8 - 243
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 &nbsp;
-												li &nbsp;
-												li &nbsp;
-												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() }}&currency={{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}}&currency={{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 &nbsp;
-												li &nbsp;
-												li &nbsp;
-												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 }}&currency={{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 }}&currency={{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
-							|  &nbsp; &nbsp; 
-							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

+ 1 - 1
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)

+ 4 - 0
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:'&angst;land Islandscode:'}
 		]
+
+		sixpack.participate 'plans', ['default', 'more-details'], (chosenVariation, rawResponse)->
+			$scope.plansVariant = chosenVariation

+ 40 - 29
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
+

BIN
services/web/public/img/advocates/erdogmus.jpg


BIN
services/web/public/img/advocates/henderson.jpg


+ 264 - 1
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
+	}
+}

+ 3 - 0
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;
+}
 

+ 13 - 1
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;
   }