diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 3aa6f919e6..5dfbeceb99 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -121,6 +121,7 @@ async function userSubscriptionPage(req, res) { const data = { title: 'your_subscription', plans, + groupPlans: GroupPlansData, user, hasSubscription, fromPlansPage, diff --git a/services/web/app/views/subscriptions/_modal_group_upgrade.pug b/services/web/app/views/subscriptions/_modal_group_upgrade.pug new file mode 100644 index 0000000000..a6320af348 --- /dev/null +++ b/services/web/app/views/subscriptions/_modal_group_upgrade.pug @@ -0,0 +1,59 @@ +script(type="text/ng-template", id="groupPlanModalUpgradeTemplate") + .modal-header + h3 Save 30% or more with a group license + .modal-body.plans + .container-fluid + .row + .col-md-6.text-center + .circle.circle-lg + | {{ displayPrice }} + span.small / year + br + span.circle-subtext For {{ selected.size }} users + ul.list-unstyled + li Each user will have access to: + li   + li(ng-if="selected.plan_code == 'collaborator'") + strong #{translate("collabs_per_proj", {collabcount:10})} + li(ng-if="selected.plan_code == 'professional'") + strong #{translate("unlimited_collabs")} + +features_premium + .col-md-6 + form.form + .form-group + label(for='plan_code') + | Plan + select.form-control(id="plan_code", ng-model="selected.plan_code") + option(ng-repeat="plan_code in options.plan_codes", value="{{plan_code.code}}") {{ plan_code.display }} + .form-group + label(for='size') + | Number of users + select.form-control(id="size", ng-model="selected.size") + option(ng-repeat="size in options.sizes", value="{{size}}") {{ size }} + .form-group + label(for='currency') + | Currency + select.form-control(disabled id="currency", ng-model="selected.currency") + option(ng-repeat="currency in options.currencies", value="{{currency.code}}") {{ currency.display }} + .form-group + label(for='usage') + | Usage + select.form-control(id="usage", ng-model="selected.usage") + option(ng-repeat="usage in options.usages", value="{{usage.code}}") {{ usage.display }} + p.small.text-center.row-spaced-small(ng-show="selected.usage == 'educational'") + | The 40% educational discount can be used by students or faculty using Overleaf for teaching + p.small.text-center.row-spaced-small(ng-show="selected.usage == 'enterprise'") + | Save an additional 40% on groups of 10 or more with our educational discount + .modal-footer + .text-center + p + strong Your new subscription will be billed immediately to your current payment method. + hr.thin + button.btn.btn-primary.btn-lg(ng-disabled='inflight' ng-click="upgrade()") Upgrade Now + hr.thin + a( + href + ng-controller="ContactGeneralModal" + ng-click="openModal()" + ) Need more than 50 licenses? Please get in touch + diff --git a/services/web/app/views/subscriptions/dashboard.pug b/services/web/app/views/subscriptions/dashboard.pug index b4dbb05645..5dd7cbffb4 100644 --- a/services/web/app/views/subscriptions/dashboard.pug +++ b/services/web/app/views/subscriptions/dashboard.pug @@ -12,6 +12,7 @@ block append meta meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey) meta(name="ol-subscription" data-type="json" content=personalSubscription) meta(name="ol-recomendedCurrency" content=personalSubscription.recurly.currency) + meta(name="ol-groupPlans" data-type="json" content=groupPlans) block content main.content.content-alt#main-content(ng-cloak) diff --git a/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug b/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug index 06aae4fc5f..d1423ec7de 100644 --- a/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug +++ b/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug @@ -21,6 +21,8 @@ div(ng-controller="RecurlySubscriptionController") a(href, ng-click="switchToChangePlanView()", ng-if="showChangePlanButton") !{translate("change_plan")}. if (personalSubscription.pendingPlan) p #{translate("want_change_to_apply_before_plan_end")} + else if (personalSubscription.plan.groupPlan) + p #{translate("contact_support_to_change_group_subscription")} if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now()) p You're on a free trial which ends on #{personalSubscription.recurly.trialEndsAtFormatted} p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount: personalSubscription.recurly.price, collectionDate: personalSubscription.recurly.nextPaymentDueAt}, ['strong', 'strong'])} @@ -64,6 +66,19 @@ div(ng-controller="RecurlySubscriptionController") +printPlans(plans.individualMonthlyPlans) +printPlans(plans.individualAnnualPlans) + div(ng-controller="ChangePlanToGroupFormController") + h2 #{translate('looking_multiple_licenses')} + div(ng-show="isValidCurrencyForUpgrade") + span #{translate('reduce_costs_group_licenses')} + br + br + a.btn.btn-success( + href="#groups" + ng-click="openGroupPlanModal()" + ) #{translate('change_to_group_plan')} + div(ng-hide="isValidCurrencyForUpgrade") + span #{translate('contact_support_to_upgrade_to_group_subscription')} + .div(ng-controller="RecurlyCancellationController", ng-show="showCancellation").text-center p @@ -129,4 +144,7 @@ script(type='text/ng-template', id='cancelPendingPlanChangeModalTemplate') ng-click="confirmCancelPendingPlanChange()" ) span(ng-hide="inflight") #{translate("revert_pending_plan_change")} - span(ng-show="inflight") #{translate("processing")}… \ No newline at end of file + span(ng-show="inflight") #{translate("processing")}… + +include ../_plans_page_mixins +include ../_modal_group_upgrade \ No newline at end of file diff --git a/services/web/frontend/js/main/subscription-dashboard.js b/services/web/frontend/js/main/subscription-dashboard.js index 2984f060b9..1735d4fa45 100644 --- a/services/web/frontend/js/main/subscription-dashboard.js +++ b/services/web/frontend/js/main/subscription-dashboard.js @@ -91,6 +91,112 @@ App.factory('RecurlyPricing', function ($q, MultiCurrencyPricing) { } }) +App.controller('ChangePlanToGroupFormController', function ($scope, $modal) { + if (!ensureRecurlyIsSetup()) return + + const subscription = getMeta('ol-subscription') + const currency = subscription.recurly.currency + + if (['USD', 'GBP', 'EUR'].includes(currency)) { + $scope.isValidCurrencyForUpgrade = true + } + + $scope.openGroupPlanModal = function () { + const planCode = subscription.plan.planCode + $scope.defaultGroupPlan = planCode.includes('professional') + ? 'professional' + : 'collaborator' + $scope.currentPlanCurrency = currency + $modal.open({ + templateUrl: 'groupPlanModalUpgradeTemplate', + controller: 'GroupPlansModalUpgradeController', + scope: $scope, + }) + } +}) + +App.controller( + 'GroupPlansModalUpgradeController', + function ($scope, $modal, $location, $http) { + $scope.options = { + plan_codes: [ + { + display: 'Collaborator', + code: 'collaborator', + }, + { + display: 'Professional', + code: 'professional', + }, + ], + currencies: [ + { + display: 'USD ($)', + code: 'USD', + }, + { + display: 'GBP (£)', + code: 'GBP', + }, + { + display: 'EUR (€)', + code: 'EUR', + }, + ], + currencySymbols: { + USD: '$', + EUR: '€', + GBP: '£', + }, + sizes: [2, 3, 4, 5, 10, 20, 50], + usages: [ + { + display: 'Enterprise', + code: 'enterprise', + }, + { + display: 'Educational', + code: 'educational', + }, + ], + } + + $scope.prices = getMeta('ol-groupPlans') + + const currency = $scope.currentPlanCurrency + + // default selected + $scope.selected = { + plan_code: $scope.defaultGroupPlan || 'collaborator', + currency, + size: '10', + usage: 'enterprise', + } + + $scope.recalculatePrice = function () { + const { usage, plan_code, currency, size } = $scope.selected + const price = $scope.prices[usage][plan_code][currency][size] + const currencySymbol = $scope.options.currencySymbols[currency] + $scope.displayPrice = `${currencySymbol}${price}` + } + + $scope.$watch('selected', $scope.recalculatePrice, true) + $scope.recalculatePrice() + + $scope.upgrade = function () { + const { plan_code, size, usage } = $scope.selected + const body = { + _csrf: window.csrfToken, + plan_code: `group_${plan_code}_${size}_${usage}`, + } + $scope.inflight = true + $http + .post(`/user/subscription/update`, body) + .then(() => location.reload()) + } + } +) + App.controller( 'ChangePlanFormController', function ($scope, $modal, RecurlyPricing) { diff --git a/services/web/locales/en.json b/services/web/locales/en.json index c98fe052d0..d41faa6181 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1045,7 +1045,10 @@ "currently_subscribed_to_plan": "You are currently subscribed to the <0>__planName__ plan.", "your_plan_is_changing_at_term_end": "Your plan is changing to <0>__pendingPlanName__ at the end of the current billing period.", "want_change_to_apply_before_plan_end": "If you wish this change to apply before the end of your current billing period, please contact us.", + "contact_support_to_change_group_subscription": "Please contact support if you wish to change your group subscription.", + "contact_support_to_upgrade_to_group_subscription": "Please contact support if you wish to be upgraded to a group subscription.", "change_plan": "Change plan", + "change_to_group_plan": "Change to a group plan", "next_payment_of_x_collectected_on_y": "The next payment of <0>__paymentAmmount__ will be collected on <1>__collectionDate__.", "additional_licenses": "Your subscription includes <0>__additionalLicenses__ additional license(s) for a total of <1>__totalLicenses__ licenses.", "pending_additional_licenses": "Your subscription is changing to include <0>__pendingAdditionalLicenses__ additional license(s) for a total of <1>__pendingTotalLicenses__ licenses.",