mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-25 10:10:08 +02:00
Implement checkout page 'refreshed' split test with Recurly Elements (#8320)
* Implement checkout page 'refreshed' split test with Recurly Elements Co-authored-by: Timothée Alby <timothee.alby@gmail.com> GitOrigin-RevId: 586a94c7969530f4d2246e9105ef48e868e9510e
This commit is contained in:
@@ -154,10 +154,25 @@ async function paymentPage(req, res) {
|
||||
res,
|
||||
'payment-page'
|
||||
)
|
||||
const template =
|
||||
const useUpdatedPaymentPage =
|
||||
assignment && assignment.variant === 'updated-payment-page'
|
||||
? 'subscriptions/new-updated'
|
||||
: 'subscriptions/new'
|
||||
|
||||
const refreshedPaymentPageAssignment =
|
||||
await SplitTestHandler.promises.getAssignment(
|
||||
req,
|
||||
res,
|
||||
'payment-page-refresh'
|
||||
)
|
||||
const useRefreshedPaymentPage =
|
||||
refreshedPaymentPageAssignment &&
|
||||
refreshedPaymentPageAssignment.variant === 'refreshed-payment-page'
|
||||
|
||||
const template = useRefreshedPaymentPage
|
||||
? 'subscriptions/new-refreshed'
|
||||
: useUpdatedPaymentPage
|
||||
? 'subscriptions/new-updated'
|
||||
: 'subscriptions/new'
|
||||
|
||||
res.render(template, {
|
||||
title: 'subscribe',
|
||||
currency,
|
||||
|
||||
360
services/web/app/views/subscriptions/new-refreshed.pug
Normal file
360
services/web/app/views/subscriptions/new-refreshed.pug
Normal file
@@ -0,0 +1,360 @@
|
||||
extends ../layout
|
||||
|
||||
block vars
|
||||
- var suppressNavbarRight = true
|
||||
- var suppressFooter = true
|
||||
|
||||
block append meta
|
||||
meta(name="ol-countryCode" content=countryCode)
|
||||
meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey)
|
||||
meta(name="ol-recommendedCurrency" content=String(currency).slice(0,3))
|
||||
|
||||
block head-scripts
|
||||
script(type="text/javascript", nonce=scriptNonce, src="https://js.recurly.com/v4/recurly.js")
|
||||
|
||||
block content
|
||||
main.content.content-alt#main-content
|
||||
.container(ng-controller="NewSubscriptionController" ng-cloak)
|
||||
.row.card-group
|
||||
.col-md-3.col-md-push-1
|
||||
.card.card-highlighted
|
||||
.price-feature-description
|
||||
h4(ng-if="planName") {{planName}}
|
||||
h4(ng-if="!planName") #{plan.name}
|
||||
if plan.features
|
||||
if plan.features.collaborators === 1
|
||||
.text-small.number-of-collaborators #{translate("collabs_per_proj_single", {collabcount: 1})}
|
||||
if plan.features.collaborators === -1
|
||||
.text-small.number-of-collaborators #{translate("unlimited_collabs")}
|
||||
if plan.features.collaborators > 1
|
||||
.text-small.number-of-collaborators #{translate("collabs_per_proj", {collabcount: plan.features.collaborators})}
|
||||
.text-small #{translate("all_premium_features_including")}
|
||||
ul.small
|
||||
if plan.features.compileTimeout > 1
|
||||
li #{translate("increased_compile_timeout")}
|
||||
if plan.features.dropbox && plan.features.github
|
||||
li #{translate("sync_dropbox_github")}
|
||||
if plan.features.versioning
|
||||
li #{translate("full_doc_history")}
|
||||
if plan.features.trackChanges
|
||||
li #{translate("track_changes")}
|
||||
if plan.features.references
|
||||
li #{translate("reference_search")}
|
||||
if plan.features.mendeley || plan.features.zotero
|
||||
li #{translate("reference_sync")}
|
||||
if plan.features.symbolyPalette
|
||||
li #{translate("symboly_palette")}
|
||||
|
||||
|
||||
div.price-summary(ng-if="recurlyPrice")
|
||||
- var priceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}"};
|
||||
hr
|
||||
h4 #{translate("payment_summary")}
|
||||
div.small
|
||||
.price-summary-line
|
||||
span
|
||||
| {{planName}}
|
||||
span(ng-if="coupon")
|
||||
| {{ availableCurrencies[currencyCode]['symbol'] }}{{ coupon.normalPriceWithoutTax | number:2 }}
|
||||
span(ng-if="!coupon")
|
||||
| {{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.subtotal }}
|
||||
.price-summary-line(ng-if="coupon")
|
||||
span
|
||||
| {{ coupon.name }}
|
||||
span
|
||||
| –{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.discount}}
|
||||
|
||||
.price-summary-line(ng-if="taxes && taxes[0] && taxes[0].rate > 0")
|
||||
span
|
||||
| #{translate("vat")} {{taxes[0].rate * 100}}%
|
||||
span
|
||||
| {{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.tax }}
|
||||
.price-summary-line.price-summary-total-line
|
||||
span
|
||||
b {{ monthlyBilling ? '#{translate("total_per_month")}' : '#{translate("total_per_year")}'}}
|
||||
span
|
||||
b {{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}
|
||||
|
||||
|
||||
.change-currency
|
||||
div.dropdown(ng-cloak dropdown)
|
||||
button.btn.btn-link.dropdown-toggle.change-currency-toggle(
|
||||
href="#",
|
||||
data-toggle="dropdown",
|
||||
dropdown-toggle
|
||||
) Change currency
|
||||
ul.dropdown-menu(role="menu")
|
||||
li(ng-repeat="(currency, value) in limitedCurrencies")
|
||||
a(
|
||||
ng-click="changeCurrency(currency)",
|
||||
)
|
||||
span.change-currency-dropdown-selected-icon(ng-show="currency == currencyCode")
|
||||
i.fa.fa-check
|
||||
| {{currency}} ({{value['symbol']}})
|
||||
|
||||
hr.thin(ng-if="trialLength || coupon")
|
||||
div.trial-coupon-summary
|
||||
div(ng-if="trialLength")
|
||||
- var trialPriceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}", trialLen:'{{trialLength}}' };
|
||||
| !{translate("first_x_days_free_after_that_y_per_month", trialPriceVars, ['strong'] )}
|
||||
|
||||
div(ng-if="recurlyPrice")
|
||||
- var priceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}", discountMonths: "{{ coupon.discountMonths }}" };
|
||||
span(ng-if="!coupon.singleUse && coupon.discountMonths > 0 && monthlyBilling")
|
||||
| !{translate("x_price_for_y_months", priceVars, ['strong'] )}
|
||||
span(ng-if="coupon.singleUse && monthlyBilling")
|
||||
| !{translate("x_price_for_first_month", priceVars, ['strong'] )}
|
||||
span(ng-if="coupon.singleUse && !monthlyBilling")
|
||||
| !{translate("x_price_for_first_year", priceVars, ['strong'] )}
|
||||
|
||||
div(ng-if="coupon && coupon.normalPrice")
|
||||
- var noDiscountPriceAngularExp = "{{ availableCurrencies[currencyCode]['symbol']}}{{coupon.normalPrice | number:2 }}";
|
||||
span(ng-if="!coupon.singleUse && coupon.discountMonths > 0 && monthlyBilling")
|
||||
| !{translate("then_x_price_per_month", { price: noDiscountPriceAngularExp } )}
|
||||
span(ng-if="!coupon.singleUse && !coupon.discountMonths && monthlyBilling")
|
||||
| !{translate("normally_x_price_per_month", { price: noDiscountPriceAngularExp } )}
|
||||
span(ng-if="!coupon.singleUse && !monthlyBilling")
|
||||
| !{translate("normally_x_price_per_year", { price: noDiscountPriceAngularExp } )}
|
||||
span(ng-if="coupon.singleUse && monthlyBilling")
|
||||
| !{translate("then_x_price_per_month", { price: noDiscountPriceAngularExp } )}
|
||||
span(ng-if="coupon.singleUse && !monthlyBilling")
|
||||
| !{translate("then_x_price_per_year", { price: noDiscountPriceAngularExp } )}
|
||||
hr.thin
|
||||
|
||||
p.price-cancel-anytime.text-center(ng-non-bindable) !{translate("cancel_anytime", { appName:'{{settings.appName}}' })}
|
||||
|
||||
.col-md-5.col-md-push-1
|
||||
.card.card-highlighted.card-border(ng-hide="threeDSecureFlow")
|
||||
.alert.alert-danger(ng-show="recurlyLoadError")
|
||||
strong #{translate('payment_provider_unreachable_error')}
|
||||
.price-switch-header(ng-hide="recurlyLoadError")
|
||||
.row
|
||||
.col-xs-9
|
||||
h2 #{translate('select_a_payment_method')}
|
||||
.row(ng-if="planCode == 'student-annual' || planCode == 'student-monthly' || planCode == 'student_free_trial_7_days'")
|
||||
.col-xs-12
|
||||
p.student-disclaimer #{translate('student_disclaimer')}
|
||||
|
||||
.row(ng-hide="recurlyLoadError")
|
||||
div()
|
||||
.col-md-12()
|
||||
form(
|
||||
name="simpleCCForm"
|
||||
novalidate
|
||||
)
|
||||
.alert.alert-warning.small(ng-show="genericError")
|
||||
strong {{genericError}}
|
||||
|
||||
.alert.alert-warning.small(ng-show="couponError")
|
||||
strong {{couponError}}
|
||||
|
||||
div
|
||||
.form-group.payment-method-toggle
|
||||
hr.thin
|
||||
.radio
|
||||
.col-xs-8
|
||||
label
|
||||
input(
|
||||
type="radio"
|
||||
ng-model="paymentMethod.value"
|
||||
name="payment_method"
|
||||
checked=true
|
||||
value="credit_card"
|
||||
)
|
||||
strong
|
||||
| #{translate("card_payment")}
|
||||
span.hidden-xs
|
||||
|
|
||||
i.fa.fa-cc-visa(aria-hidden="true")
|
||||
|
|
||||
i.fa.fa-cc-mastercard(aria-hidden="true")
|
||||
|
|
||||
i.fa.fa-cc-amex(aria-hidden="true")
|
||||
|
||||
.col-xs-4
|
||||
label
|
||||
input(
|
||||
type="radio"
|
||||
ng-model="paymentMethod.value"
|
||||
name="payment_method"
|
||||
checked=false
|
||||
value="paypal"
|
||||
)
|
||||
strong PayPal
|
||||
span.hidden-xs
|
||||
|
|
||||
i.fa.fa-cc-paypal(aria-hidden="true")
|
||||
|
||||
div(ng-show="paymentMethod.value === 'credit_card'")
|
||||
.form-group(ng-class="showCardElementInvalid ? 'has-error' : ''")
|
||||
label(for="recurly-card-input") #{translate("card_details")}
|
||||
div#recurly-card-input
|
||||
span.input-feedback-message(ng-if="showCardElementInvalid") Card details are not valid
|
||||
|
||||
.row
|
||||
.col-xs-6
|
||||
.form-group(ng-class="validation.errorFields.first_name || inputHasError(simpleCCForm.firstName) ? 'has-error' : ''")
|
||||
label(for="first-name") #{translate('first_name')}
|
||||
input#first-name.form-control(
|
||||
type="text"
|
||||
maxlength='255'
|
||||
data-recurly="first_name"
|
||||
name="firstName"
|
||||
ng-model="data.first_name"
|
||||
required
|
||||
)
|
||||
span.input-feedback-message(ng-if="simpleCCForm.firstName.$error.required") #{translate('this_field_is_required')}
|
||||
.col-xs-6
|
||||
.form-group(ng-class="validation.errorFields.last_name || inputHasError(simpleCCForm.lastName)? 'has-error' : ''")
|
||||
label(for="last-name") #{translate('last_name')}
|
||||
input#last-name.form-control(
|
||||
type="text"
|
||||
maxlength='255'
|
||||
data-recurly="last_name"
|
||||
name="lastName"
|
||||
ng-model="data.last_name"
|
||||
required
|
||||
)
|
||||
span.input-feedback-message(ng-if="simpleCCForm.lastName.$error.required") #{translate('this_field_is_required')}
|
||||
|
||||
div
|
||||
.row
|
||||
.col-xs-12
|
||||
.form-group(ng-class="validation.errorFields.address1 || inputHasError(simpleCCForm.address1) ? 'has-error' : ''")
|
||||
label(for="address-line-1") #{translate('address_line_1')}
|
||||
i.fa.fa-question-circle(
|
||||
aria-label=translate('this_address_will_be_shown_on_the_invoice'),
|
||||
tooltip=translate('this_address_will_be_shown_on_the_invoice'),
|
||||
tooltip-placement="right",
|
||||
tooltip-append-to-body="true",
|
||||
)
|
||||
input#address-line-1.form-control(
|
||||
type="text"
|
||||
maxlength="255"
|
||||
data-recurly="address1"
|
||||
name="address1"
|
||||
ng-model="data.address1"
|
||||
required
|
||||
)
|
||||
span.input-feedback-message(ng-if="simpleCCForm.address1.$error.required") #{translate('this_field_is_required')}
|
||||
|
||||
.row.toggle-address-second-line(ng-hide="ui.showAddressSecondLine")
|
||||
.col-xs-12
|
||||
a.text-small(
|
||||
href="#"
|
||||
ng-click="showAddressSecondLine($event)"
|
||||
) + Add another address line
|
||||
|
||||
.row(ng-show="ui.showAddressSecondLine")
|
||||
.col-xs-12
|
||||
.form-group.has-feedback(ng-class="validation.errorFields.address2 ? 'has-error' : ''")
|
||||
label(for="address-line-2") #{translate('address_second_line_optional')}
|
||||
input#address-line-2.form-control(
|
||||
type="text"
|
||||
maxlength="255"
|
||||
data-recurly="address2"
|
||||
name="address2"
|
||||
ng-model="data.address2"
|
||||
)
|
||||
|
||||
.row
|
||||
.col-xs-4
|
||||
.form-group(ng-class="validation.errorFields.postal_code || inputHasError(simpleCCForm.postalCode) ? 'has-error' : ''")
|
||||
label(for="postal-code") #{translate('postal_code')}
|
||||
input#postal-code.form-control(
|
||||
type="text"
|
||||
maxlength="255"
|
||||
data-recurly="postal_code"
|
||||
name="postalCode"
|
||||
ng-model="data.postal_code"
|
||||
required
|
||||
)
|
||||
span.input-feedback-message(ng-if="simpleCCForm.postalCode.$error.required") #{translate('this_field_is_required')}
|
||||
|
||||
.col-xs-8
|
||||
.form-group(ng-class="validation.errorFields.country || inputHasError(simpleCCForm.country) ? 'has-error' : ''")
|
||||
label(for="country") #{translate('country')}
|
||||
select#country.form-control(
|
||||
data-recurly="country"
|
||||
ng-model="data.country"
|
||||
name="country"
|
||||
ng-change="updateCountry()"
|
||||
ng-selected="{{country.code == data.country}}"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
required
|
||||
)
|
||||
option(value='', disabled) #{translate("country")}
|
||||
option(value='-', disabled) --------------
|
||||
option(ng-repeat="country in countries" ng-bind-html="country.name" value="{{country.code}}")
|
||||
span.input-feedback-message(ng-if="simpleCCForm.country.$error.required") #{translate('this_field_is_required')}
|
||||
|
||||
.form-group
|
||||
.checkbox
|
||||
label
|
||||
input(
|
||||
type="checkbox"
|
||||
ng-model="ui.addCompanyDetails"
|
||||
)
|
||||
|
|
||||
| #{translate("add_company_details")}
|
||||
|
||||
.form-group(ng-show="ui.addCompanyDetails")
|
||||
label(for="company-name") #{translate("company_name")}
|
||||
input#company-name.form-control(
|
||||
type="text"
|
||||
name="companyName"
|
||||
ng-model="data.company"
|
||||
)
|
||||
|
||||
.form-group(ng-show="ui.addCompanyDetails && taxes.length")
|
||||
label(for="vat-number") #{translate("vat_number")}
|
||||
input#vat-number.form-control(
|
||||
type="text"
|
||||
name="vatNumber"
|
||||
ng-model="data.vat_number"
|
||||
ng-blur="applyVatNumber()"
|
||||
)
|
||||
|
||||
if (showCouponField)
|
||||
.form-group
|
||||
label(for="coupon-code") #{translate('coupon_code')}
|
||||
input#coupon-code.form-control(
|
||||
type="text"
|
||||
ng-blur="applyCoupon()"
|
||||
ng-model="data.coupon"
|
||||
)
|
||||
|
||||
|
||||
p(ng-if="paymentMethod.value === 'paypal'") #{translate("proceeding_to_paypal_takes_you_to_the_paypal_site_to_pay")}
|
||||
|
||||
hr.thin
|
||||
|
||||
div.payment-submit
|
||||
button.btn.btn-success.btn-block(
|
||||
ng-click="submit()"
|
||||
ng-disabled="processing || !isFormValid(simpleCCForm);"
|
||||
)
|
||||
span(ng-show="processing")
|
||||
i.fa.fa-spinner.fa-spin(aria-hidden="true")
|
||||
span.sr-only #{translate('processing')}
|
||||
|
|
||||
span(ng-if="paymentMethod.value === 'credit_card'")
|
||||
| {{ trialLength ? '#{translate("upgrade_cc_btn")}' : '#{translate("upgrade_now")}'}}
|
||||
span(ng-if="paymentMethod.value !== 'credit_card'") #{translate("proceed_to_paypal")}
|
||||
|
||||
p.tos-agreement-notice !{translate("by_subscribing_you_agree_to_our_terms_of_service", {}, [{name: 'a', attrs: {href: '/legal#Terms', target:'_blank', rel:'noopener noreferrer'}}])}
|
||||
|
||||
div.three-d-secure-container.card.card-highlighted.card-border(ng-show="threeDSecureFlow")
|
||||
.alert.alert-info.small(aria-live="assertive")
|
||||
strong #{translate('card_must_be_authenticated_by_3dsecure')}
|
||||
div.three-d-secure-recurly-container
|
||||
|
||||
script(type="text/javascript", nonce=scriptNonce).
|
||||
ga('send', 'event', 'pageview', 'payment_form', "#{plan_code}")
|
||||
|
||||
script(
|
||||
type="text/ng-template"
|
||||
id="cvv-tooltip-tpl.html"
|
||||
)
|
||||
p !{translate("for_visa_mastercard_and_discover", {}, ['strong', 'strong', 'strong'])}
|
||||
p !{translate("for_american_express", {}, ['strong', 'strong', 'strong'])}
|
||||
@@ -22,11 +22,14 @@ export default App.controller(
|
||||
}
|
||||
|
||||
$scope.ui = {
|
||||
showCurrencyDropdown: false,
|
||||
showAddressSecondLine: false,
|
||||
addCompanyDetails: false,
|
||||
}
|
||||
|
||||
$scope.recurlyLoadError = false
|
||||
$scope.currencyCode = MultiCurrencyPricing.currencyCode
|
||||
$scope.initiallySelectedCurrencyCode = MultiCurrencyPricing.currencyCode // track for payment-page-refreshed
|
||||
$scope.allCurrencies = MultiCurrencyPricing.plans
|
||||
$scope.availableCurrencies = {}
|
||||
$scope.planCode = window.plan_code
|
||||
@@ -157,6 +160,20 @@ export default App.controller(
|
||||
}
|
||||
}
|
||||
|
||||
// abridged list for payment-page-refreshed split test
|
||||
$scope.limitedCurrencies = {}
|
||||
const limitedCurrencyCodes = ['USD', 'EUR', 'GBP']
|
||||
if (
|
||||
limitedCurrencyCodes.indexOf($scope.initiallySelectedCurrencyCode) ===
|
||||
-1
|
||||
) {
|
||||
limitedCurrencyCodes.unshift($scope.initiallySelectedCurrencyCode)
|
||||
}
|
||||
limitedCurrencyCodes.forEach(currencyCode => {
|
||||
$scope.limitedCurrencies[currencyCode] =
|
||||
MultiCurrencyPricing.plans[currencyCode]
|
||||
})
|
||||
|
||||
if (
|
||||
pricing.items &&
|
||||
pricing.items.coupon &&
|
||||
@@ -208,6 +225,41 @@ export default App.controller(
|
||||
.done()
|
||||
}
|
||||
|
||||
$scope.showAddressSecondLine = function (e) {
|
||||
e.preventDefault()
|
||||
$scope.ui.showAddressSecondLine = true
|
||||
}
|
||||
|
||||
$scope.showCurrencyDropdown = function (e) {
|
||||
e.preventDefault()
|
||||
$scope.ui.showCurrencyDropdown = true
|
||||
}
|
||||
|
||||
// This check is just so we don't load this on the default checkout variant
|
||||
const newCardInputElement = document.querySelector('#recurly-card-input')
|
||||
const elements = recurly.Elements()
|
||||
if (newCardInputElement) {
|
||||
const card = elements.CardElement({
|
||||
displayIcon: true,
|
||||
style: {
|
||||
inputType: 'mobileSelect',
|
||||
fontColor: '#5d6879',
|
||||
placeholder: {},
|
||||
invalid: {
|
||||
fontColor: '#a93529',
|
||||
},
|
||||
},
|
||||
})
|
||||
card.attach('#recurly-card-input')
|
||||
card.on('change', state => {
|
||||
$scope.$applyAsync(() => {
|
||||
$scope.showCardElementInvalid =
|
||||
!state.focus && !state.empty && !state.valid
|
||||
$scope.cardIsValid = state.valid
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
$scope.applyVatNumber = () =>
|
||||
pricing
|
||||
.tax({ tax_code: 'digital', vat_number: $scope.data.vat_number })
|
||||
@@ -242,7 +294,11 @@ export default App.controller(
|
||||
if ($scope.paymentMethod.value === 'paypal') {
|
||||
return $scope.data.country !== ''
|
||||
} else {
|
||||
return form.$valid
|
||||
if (newCardInputElement) {
|
||||
return form.$valid && $scope.cardIsValid
|
||||
} else {
|
||||
return form.$valid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +423,11 @@ export default App.controller(
|
||||
delete tokenData.company
|
||||
delete tokenData.vat_number
|
||||
}
|
||||
recurly.token(tokenData, completeSubscription)
|
||||
if (newCardInputElement) {
|
||||
recurly.token(elements, tokenData, completeSubscription)
|
||||
} else {
|
||||
recurly.token(tokenData, completeSubscription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
.recurly-hosted-field {
|
||||
&:extend(.form-control);
|
||||
}
|
||||
|
||||
.recurly-element-card {
|
||||
&:extend(.form-control);
|
||||
padding: 4px 4px;
|
||||
border: 1px #cccccc solid;
|
||||
border-radius: 20px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.recurly-element-card-invalid {
|
||||
&:extend(.has-error);
|
||||
border-color: @red;
|
||||
}
|
||||
|
||||
.recurly-element-number {
|
||||
&:extend(.form-control);
|
||||
}
|
||||
|
||||
@@ -102,15 +102,24 @@
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
h4 {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
ul {
|
||||
padding-left: 10px;
|
||||
}
|
||||
li {
|
||||
list-style-position: inside;
|
||||
}
|
||||
.number-of-collaborators {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.price-summary {
|
||||
padding-bottom: 7.5px;
|
||||
|
||||
.price-summary-line {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -119,6 +128,10 @@
|
||||
margin-top: 5px;
|
||||
font-size: 16px;
|
||||
}
|
||||
hr {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.price-details-spacing {
|
||||
@@ -128,3 +141,37 @@
|
||||
.price-cancel-anytime {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.trial-coupon-summary {
|
||||
font-size: 12px;
|
||||
padding-top: 7.5px;
|
||||
padding-bottom: 7.5px;
|
||||
}
|
||||
|
||||
.toggle-address-second-line {
|
||||
margin-bottom: @line-height-computed / 2;
|
||||
}
|
||||
|
||||
.payment-method-toggle {
|
||||
border-bottom: 1px solid @hr-border;
|
||||
}
|
||||
|
||||
.change-currency {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.change-currency-toggle {
|
||||
padding-left: 0px;
|
||||
font-size: 12px;
|
||||
text-decoration: underline;
|
||||
color: @text-small-color;
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @text-small-color;
|
||||
}
|
||||
}
|
||||
|
||||
.change-currency-dropdown-selected-icon {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
"github_workflow_files_delete_github_repo": "The repository has been created on GitHub but linking was unsuccessful. You will have to delete GitHub repository or choose a new name.",
|
||||
"address_line_1": "Address",
|
||||
"address_line_2": "Address (line 2, optional)",
|
||||
"address_second_line_optional": "Address second line (optional)",
|
||||
"this_address_will_be_shown_on_the_invoice": "This address will be shown on the invoice",
|
||||
"postal_code": "Postal Code",
|
||||
"reload_editor": "Reload editor",
|
||||
"compile_error_description": "This project did not compile because of an error",
|
||||
@@ -92,6 +94,7 @@
|
||||
"beta_feature_badge": "Beta feature badge",
|
||||
"invalid_filename": "Upload failed: check that the file name doesn’t contain special characters, trailing/leading whitespace or more than __nameLimit__ characters",
|
||||
"clsi_unavailable": "Sorry, the compile server for your project was temporarily unavailable. Please try again in a few moments.",
|
||||
"first_x_days_free_after_that_y_per_month": "First <0>__trialLen__ days free</0>, after that <0>__price__</0> per month",
|
||||
"x_price_per_month": "<0>__price__</0> per month",
|
||||
"x_price_per_year": "<0>__price__</0> per year",
|
||||
"x_price_for_first_month": "<0>__price__</0> for your first month",
|
||||
@@ -720,8 +723,10 @@
|
||||
"your_billing_details_were_saved": "Your billing details were saved",
|
||||
"security_code": "Security code",
|
||||
"paypal_upgrade": "To upgrade, click on the button below and log on to PayPal using your email and password.",
|
||||
"proceeding_to_paypal_takes_you_to_the_paypal_site_to_pay": "Proceeding to PayPal will take you to the PayPal site to pay for your subscription.",
|
||||
"upgrade_cc_btn": "Upgrade now, pay after 7 days",
|
||||
"upgrade_paypal_btn": "Continue",
|
||||
"proceed_to_paypal": "Proceed to PayPal",
|
||||
"notification_project_invite": "<b>__userName__</b> would like you to join <b>__projectName__</b> <a class=\"btn btn-sm btn-info pull-right\" href=\"/project/__projectId__/invite/token/__token__\">Join Project</a>",
|
||||
"file_restored": "Your file (__filename__) has been recovered.",
|
||||
"file_restored_back_to_editor": "You can go back to the editor and work on it again.",
|
||||
@@ -1006,6 +1011,7 @@
|
||||
"one_collaborator": "Only one collaborator",
|
||||
"collaborator": "Collaborator",
|
||||
"standard": "Standard",
|
||||
"all_premium_features_including": "All premium features, including:",
|
||||
"collabs_per_proj": "__collabcount__ collaborators per project",
|
||||
"full_doc_history": "Full document history",
|
||||
"sync_to_dropbox": "Sync to Dropbox",
|
||||
@@ -1398,8 +1404,11 @@
|
||||
"about_brian_gough": "is a software developer and former theoretical high energy physicist at Fermilab and Los Alamos. For many years he published free software manuals commercially using TeX and LaTeX and was also the maintainer of the GNU Scientific Library.",
|
||||
"first_few_days_free": "First __trialLen__ days free",
|
||||
"every": "per",
|
||||
"select_a_payment_method": "Select a payment method",
|
||||
"credit_card": "Credit Card",
|
||||
"card_payment": "Card payment",
|
||||
"credit_card_number": "Credit Card Number",
|
||||
"card_details": "Card details",
|
||||
"invalid": "Invalid",
|
||||
"expiry": "Expiry Date",
|
||||
"january": "January",
|
||||
|
||||
Reference in New Issue
Block a user