Browse Source

Merge pull request #13311 from overleaf/ab-tear-down-subscription-pages-react

[web] Tear down subscription-pages-react test and remove Angular code

GitOrigin-RevId: 3cf906e476ffa52a058ccb4e4acbb89a657bd021
Alexandre Bourdin 3 years ago
parent
commit
150cf21710
27 changed files with 50 additions and 2767 deletions
  1. 4 222
      services/web/app/src/Features/Subscription/SubscriptionController.js
  2. 27 134
      services/web/app/src/Features/UserMembership/UserMembershipController.js
  3. 0 15
      services/web/app/views/subscriptions/canceled-subscription.pug
  4. 0 68
      services/web/app/views/subscriptions/dashboard.pug
  5. 0 28
      services/web/app/views/subscriptions/dashboard/_change_plans_mixins.pug
  6. 0 32
      services/web/app/views/subscriptions/dashboard/_group_memberships.pug
  7. 0 10
      services/web/app/views/subscriptions/dashboard/_institution_memberships.pug
  8. 0 24
      services/web/app/views/subscriptions/dashboard/_managed_groups.pug
  9. 0 24
      services/web/app/views/subscriptions/dashboard/_managed_institutions.pug
  10. 0 16
      services/web/app/views/subscriptions/dashboard/_managed_publishers.pug
  11. 0 7
      services/web/app/views/subscriptions/dashboard/_personal_subscription.pug
  12. 0 6
      services/web/app/views/subscriptions/dashboard/_personal_subscription_custom.pug
  13. 0 159
      services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug
  14. 0 18
      services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly_sync_email.pug
  15. 0 9
      services/web/app/views/subscriptions/dashboard/_team_name_mixin.pug
  16. 0 62
      services/web/app/views/subscriptions/dashboard/_v1_subscription_status.pug
  17. 0 364
      services/web/app/views/subscriptions/new-refreshed.pug
  18. 0 40
      services/web/app/views/subscriptions/successful-subscription.pug
  19. 0 119
      services/web/app/views/user_membership/index.pug
  20. 0 4
      services/web/frontend/js/main.js
  21. 0 793
      services/web/frontend/js/main/new-subscription.js
  22. 0 451
      services/web/frontend/js/main/subscription-dashboard.js
  23. 0 13
      services/web/frontend/js/main/subscription/upgrade-subscription.js
  24. 0 126
      services/web/frontend/js/main/user-membership.js
  25. 0 2
      services/web/locales/en.json
  26. 14 5
      services/web/test/unit/src/Subscription/SubscriptionControllerTests.js
  27. 5 16
      services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js

+ 4 - 222
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -129,34 +129,12 @@ async function plansPage(req, res) {
   })
   })
 }
 }
 
 
-async function paymentPage(req, res) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    // get to show the recurly.js page
-    if (assignment.variant === 'active') {
-      await _paymentReactPage(req, res)
-    } else {
-      await _paymentAngularPage(req, res)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _paymentAngularPage(req, res)
-  }
-}
-
 /**
 /**
  * @param {import("express").Request} req
  * @param {import("express").Request} req
  * @param {import("express").Response} res
  * @param {import("express").Response} res
  * @returns {Promise<void>}
  * @returns {Promise<void>}
  */
  */
-async function _paymentReactPage(req, res) {
+async function paymentPage(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
   const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
   if (!plan) {
   if (!plan) {
@@ -211,82 +189,6 @@ async function _paymentReactPage(req, res) {
   }
   }
 }
 }
 
 
-async function _paymentAngularPage(req, res) {
-  const user = SessionManager.getSessionUser(req.session)
-  const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
-  if (!plan) {
-    return HttpErrorHandler.unprocessableEntity(req, res, 'Plan not found')
-  }
-  const hasSubscription =
-    await LimitationsManager.promises.userHasV1OrV2Subscription(user)
-  if (hasSubscription) {
-    res.redirect('/user/subscription?hasSubscription=true')
-  } else {
-    // LimitationsManager.userHasV2Subscription only checks Mongo. Double check with
-    // Recurly as well at this point (we don't do this most places for speed).
-    const valid =
-      await SubscriptionHandler.promises.validateNoSubscriptionInRecurly(
-        user._id
-      )
-    if (!valid) {
-      res.redirect('/user/subscription?hasSubscription=true')
-    } else {
-      let currency = null
-      if (req.query.currency) {
-        const queryCurrency = req.query.currency.toUpperCase()
-        if (GeoIpLookup.isValidCurrencyParam(queryCurrency)) {
-          currency = queryCurrency
-        }
-      }
-      const { currencyCode: recommendedCurrency, countryCode } =
-        await GeoIpLookup.promises.getCurrencyCode(req.query?.ip || req.ip)
-      if (recommendedCurrency && currency == null) {
-        currency = recommendedCurrency
-      }
-
-      await SplitTestHandler.promises.getAssignment(
-        req,
-        res,
-        'student-check-modal'
-      )
-
-      res.render('subscriptions/new-refreshed', {
-        title: 'subscribe',
-        currency,
-        countryCode,
-        plan,
-        recurlyConfig: JSON.stringify({
-          currency,
-          subdomain: Settings.apis.recurly.subdomain,
-        }),
-        showCouponField: !!req.query.scf,
-        showVatField: !!req.query.svf,
-      })
-    }
-  }
-}
-
-async function userSubscriptionPage(req, res) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _userSubscriptionReactPage(req, res)
-    } else {
-      await _userSubscriptionAngularPage(req, res)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _userSubscriptionAngularPage(req, res)
-  }
-}
-
 function formatGroupPlansDataForDash() {
 function formatGroupPlansDataForDash() {
   return {
   return {
     plans: [...groupPlanModalOptions.plan_codes],
     plans: [...groupPlanModalOptions.plan_codes],
@@ -301,7 +203,7 @@ function formatGroupPlansDataForDash() {
  * @param {import("express").Response} res
  * @param {import("express").Response} res
  * @returns {Promise<void>}
  * @returns {Promise<void>}
  */
  */
-async function _userSubscriptionReactPage(req, res) {
+async function userSubscriptionPage(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const results =
   const results =
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
@@ -356,58 +258,6 @@ async function _userSubscriptionReactPage(req, res) {
   res.render('subscriptions/dashboard-react', data)
   res.render('subscriptions/dashboard-react', data)
 }
 }
 
 
-async function _userSubscriptionAngularPage(req, res) {
-  const user = SessionManager.getSessionUser(req.session)
-  const results =
-    await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
-      user
-    )
-  const {
-    personalSubscription,
-    memberGroupSubscriptions,
-    managedGroupSubscriptions,
-    currentInstitutionsWithLicence,
-    managedInstitutions,
-    managedPublishers,
-    v1SubscriptionStatus,
-  } = results
-  const hasSubscription =
-    await LimitationsManager.promises.userHasV1OrV2Subscription(user)
-  const fromPlansPage = req.query.hasSubscription
-  const plans = SubscriptionViewModelBuilder.buildPlansList(
-    personalSubscription ? personalSubscription.plan : undefined
-  )
-
-  AnalyticsManager.recordEventForSession(req.session, 'subscription-page-view')
-
-  const cancelButtonAssignment = await SplitTestHandler.promises.getAssignment(
-    req,
-    res,
-    'subscription-cancel-button'
-  )
-
-  const cancelButtonNewCopy = cancelButtonAssignment?.variant === 'new-copy'
-
-  const data = {
-    title: 'your_subscription',
-    plans,
-    groupPlans: GroupPlansData,
-    user,
-    hasSubscription,
-    fromPlansPage,
-    personalSubscription,
-    memberGroupSubscriptions,
-    managedGroupSubscriptions,
-    managedInstitutions,
-    managedPublishers,
-    v1SubscriptionStatus,
-    currentInstitutionsWithLicence,
-    groupPlanModalOptions,
-    cancelButtonNewCopy,
-  }
-  res.render('subscriptions/dashboard', data)
-}
-
 async function interstitialPaymentPage(req, res) {
 async function interstitialPaymentPage(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const { recommendedCurrency, countryCode, geoPricingTestVariant } =
   const { recommendedCurrency, countryCode, geoPricingTestVariant } =
@@ -515,33 +365,12 @@ async function createSubscription(req, res) {
   }
   }
 }
 }
 
 
-async function successfulSubscription(req, res) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _successfulSubscriptionReact(req, res)
-    } else {
-      await _successfulSubscriptionAngular(req, res)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _successfulSubscriptionAngular(req, res)
-  }
-}
-
 /**
 /**
  * @param {import("express").Request} req
  * @param {import("express").Request} req
  * @param {import("express").Response} res
  * @param {import("express").Response} res
  * @returns {Promise<void>}
  * @returns {Promise<void>}
  */
  */
-async function _successfulSubscriptionReact(req, res) {
+async function successfulSubscription(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const { personalSubscription } =
   const { personalSubscription } =
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
@@ -561,26 +390,6 @@ async function _successfulSubscriptionReact(req, res) {
   }
   }
 }
 }
 
 
-async function _successfulSubscriptionAngular(req, res) {
-  const user = SessionManager.getSessionUser(req.session)
-  const { personalSubscription } =
-    await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
-      user
-    )
-
-  const postCheckoutRedirect = req.session?.postCheckoutRedirect
-
-  if (!personalSubscription) {
-    res.redirect('/user/subscription/plans')
-  } else {
-    res.render('subscriptions/successful-subscription', {
-      title: 'thank_you',
-      personalSubscription,
-      postCheckoutRedirect,
-    })
-  }
-}
-
 function cancelSubscription(req, res, next) {
 function cancelSubscription(req, res, next) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   logger.debug({ userId: user._id }, 'canceling subscription')
   logger.debug({ userId: user._id }, 'canceling subscription')
@@ -597,45 +406,18 @@ function cancelSubscription(req, res, next) {
   })
   })
 }
 }
 
 
-async function canceledSubscription(req, res, next) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _canceledSubscriptionReact(req, res, next)
-    } else {
-      await _canceledSubscriptionAngular(req, res, next)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _canceledSubscriptionAngular(req, res, next)
-  }
-}
-
 /**
 /**
  * @param {import("express").Request} req
  * @param {import("express").Request} req
  * @param {import("express").Response} res
  * @param {import("express").Response} res
  * @param {import("express").NextFunction} next
  * @param {import("express").NextFunction} next
  * @returns {Promise<void>}
  * @returns {Promise<void>}
  */
  */
-function _canceledSubscriptionReact(req, res, next) {
+function canceledSubscription(req, res, next) {
   return res.render('subscriptions/canceled-subscription-react', {
   return res.render('subscriptions/canceled-subscription-react', {
     title: 'subscription_canceled',
     title: 'subscription_canceled',
   })
   })
 }
 }
 
 
-function _canceledSubscriptionAngular(req, res, next) {
-  return res.render('subscriptions/canceled-subscription', {
-    title: 'subscription_canceled',
-  })
-}
-
 function cancelV1Subscription(req, res, next) {
 function cancelV1Subscription(req, res, next) {
   const userId = SessionManager.getLoggedInUserId(req.session)
   const userId = SessionManager.getLoggedInUserId(req.session)
   logger.debug({ userId }, 'canceling v1 subscription')
   logger.debug({ userId }, 'canceling v1 subscription')

+ 27 - 134
services/web/app/src/Features/UserMembership/UserMembershipController.js

@@ -16,110 +16,9 @@ const Errors = require('../Errors/Errors')
 const EmailHelper = require('../Helpers/EmailHelper')
 const EmailHelper = require('../Helpers/EmailHelper')
 const { csvAttachment } = require('../../infrastructure/Response')
 const { csvAttachment } = require('../../infrastructure/Response')
 const { UserIsManagerError } = require('./UserMembershipErrors')
 const { UserIsManagerError } = require('./UserMembershipErrors')
-const SplitTestHandler = require('../SplitTests/SplitTestHandler')
 const CSVParser = require('json2csv').Parser
 const CSVParser = require('json2csv').Parser
-const logger = require('@overleaf/logger')
 
 
 async function manageGroupMembers(req, res, next) {
 async function manageGroupMembers(req, res, next) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _manageGroupMembersReact(req, res, next)
-    } else {
-      await _indexAngular(req, res, next)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _indexAngular(req, res, next)
-  }
-}
-
-async function manageGroupManagers(req, res, next) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _renderManagersPage(
-        req,
-        res,
-        next,
-        'user_membership/group-managers-react'
-      )
-    } else {
-      await _indexAngular(req, res, next)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _indexAngular(req, res, next)
-  }
-}
-
-async function manageInstitutionManagers(req, res, next) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _renderManagersPage(
-        req,
-        res,
-        next,
-        'user_membership/institution-managers-react'
-      )
-    } else {
-      await _indexAngular(req, res, next)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _indexAngular(req, res, next)
-  }
-}
-
-async function managePublisherManagers(req, res, next) {
-  try {
-    const assignment = await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'subscription-pages-react'
-    )
-    if (assignment.variant === 'active') {
-      await _renderManagersPage(
-        req,
-        res,
-        next,
-        'user_membership/publisher-managers-react'
-      )
-    } else {
-      await _indexAngular(req, res, next)
-    }
-  } catch (error) {
-    logger.warn(
-      { err: error },
-      'failed to get "subscription-pages-react" split test assignment'
-    )
-    await _indexAngular(req, res, next)
-  }
-}
-
-async function _manageGroupMembersReact(req, res, next) {
   const { entity, entityConfig } = req
   const { entity, entityConfig } = req
   return entity.fetchV1Data(function (error, entity) {
   return entity.fetchV1Data(function (error, entity) {
     if (error != null) {
     if (error != null) {
@@ -149,6 +48,33 @@ async function _manageGroupMembersReact(req, res, next) {
   })
   })
 }
 }
 
 
+async function manageGroupManagers(req, res, next) {
+  await _renderManagersPage(
+    req,
+    res,
+    next,
+    'user_membership/group-managers-react'
+  )
+}
+
+async function manageInstitutionManagers(req, res, next) {
+  await _renderManagersPage(
+    req,
+    res,
+    next,
+    'user_membership/institution-managers-react'
+  )
+}
+
+async function managePublisherManagers(req, res, next) {
+  await _renderManagersPage(
+    req,
+    res,
+    next,
+    'user_membership/publisher-managers-react'
+  )
+}
+
 async function _renderManagersPage(req, res, next, template) {
 async function _renderManagersPage(req, res, next, template) {
   const { entity, entityConfig } = req
   const { entity, entityConfig } = req
   return entity.fetchV1Data(function (error, entity) {
   return entity.fetchV1Data(function (error, entity) {
@@ -178,39 +104,6 @@ async function _renderManagersPage(req, res, next, template) {
   })
   })
 }
 }
 
 
-function _indexAngular(req, res, next) {
-  const { entity, entityConfig } = req
-  return entity.fetchV1Data(function (error, entity) {
-    if (error != null) {
-      return next(error)
-    }
-    return UserMembershipHandler.getUsers(
-      entity,
-      entityConfig,
-      function (error, users) {
-        let entityName
-        if (error != null) {
-          return next(error)
-        }
-        const entityPrimaryKey =
-          entity[entityConfig.fields.primaryKey].toString()
-        if (entityConfig.fields.name) {
-          entityName = entity[entityConfig.fields.name]
-        }
-        return res.render('user_membership/index', {
-          name: entityName,
-          users,
-          groupSize: entityConfig.hasMembersLimit
-            ? entity.membersLimit
-            : undefined,
-          translations: entityConfig.translations,
-          paths: entityConfig.pathsFor(entityPrimaryKey),
-        })
-      }
-    )
-  })
-}
-
 module.exports = {
 module.exports = {
   manageGroupMembers,
   manageGroupMembers,
   manageGroupManagers,
   manageGroupManagers,

+ 0 - 15
services/web/app/views/subscriptions/canceled-subscription.pug

@@ -1,15 +0,0 @@
-extends ../layout
-
-block content
-	main.content.content-alt#main-content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.card(ng-cloak)
-						.page-header
-							h2 #{translate("subscription_canceled")}
-						.alert.alert-info
-							p #{translate("to_modify_your_subscription_go_to")} 
-								a(href="/user/subscription") #{translate("manage_subscription")}.
-						p
-							a.btn.btn-primary(href="/project") &lt; #{translate("back_to_your_projects")}

+ 0 - 68
services/web/app/views/subscriptions/dashboard.pug

@@ -1,68 +0,0 @@
-extends ../layout
-
-include ./dashboard/_team_name_mixin
-
-block head-scripts
-	script(type="text/javascript", nonce=scriptNonce, src="https://js.recurly.com/v4/recurly.js")
-
-block append meta
-	meta(name="ol-managedInstitutions", data-type="json", content=managedInstitutions)
-	meta(name="ol-planCodesChangingAtTermEnd", data-type="json", content=plans.planCodesChangingAtTermEnd)
-	if (personalSubscription && personalSubscription.recurly)
-		meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey)
-		meta(name="ol-subscription" data-type="json" content=personalSubscription)
-		meta(name="ol-recommendedCurrency" content=personalSubscription.recurly.currency)
-		meta(name="ol-groupPlans" data-type="json" content=groupPlans)
-		meta(name="ol-groupPlanModalOptions" data-type="json" content=groupPlanModalOptions)
-
-block content
-	main.content.content-alt#main-content(ng-cloak)
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					if (fromPlansPage)
-						.alert.alert-warning
-							p You already have a subscription
-					.card
-						.page-header
-							h1 #{translate("your_subscription")}
-						-var hasDisplayedSubscription = false
-						if (personalSubscription)
-							-hasDisplayedSubscription = true
-							include ./dashboard/_personal_subscription
-
-						if (managedGroupSubscriptions && managedGroupSubscriptions.length > 0)
-							include ./dashboard/_managed_groups
-
-						if (managedInstitutions && managedInstitutions.length > 0)
-							include ./dashboard/_managed_institutions
-
-						if (managedPublishers && managedPublishers.length > 0)
-							include ./dashboard/_managed_publishers
-
-						if (memberGroupSubscriptions && memberGroupSubscriptions.length > 0)
-							-hasDisplayedSubscription = true
-							include ./dashboard/_group_memberships
-
-						include ./dashboard/_institution_memberships
-
-						if (v1SubscriptionStatus)
-							include ./dashboard/_v1_subscription_status
-
-						if (!hasDisplayedSubscription)
-							if (hasSubscription)
-								-hasDisplayedSubscription = true
-								p(ng-non-bindable) You're on an #{settings.appName} Paid plan. Contact
-									a(href="mailto:support@overleaf.com") support@overleaf.com
-									|  to find out more.
-							else
-								p(ng-non-bindable)
-								| You are on the #{settings.appName} Free plan. Upgrade to access these 
-								a(href="/learn/how-to/Overleaf_premium_features") Premium Features:
-								ul
-									li #{translate('invite_more_collabs')}
-									for feature in ['realtime_track_changes', 'full_doc_history', 'reference_search', 'reference_sync', 'dropbox_integration_lowercase', 'github_integration_lowercase', 'priority_support']
-										li #{translate(feature)}
-								a(ng-controller="UpgradeSubscriptionController" href="/user/subscription/plans" ng-click="upgradeSubscription()").btn.btn-primary Upgrade now
-
-	!= moduleIncludes("contactModalGeneral", locals)

+ 0 - 28
services/web/app/views/subscriptions/dashboard/_change_plans_mixins.pug

@@ -1,28 +0,0 @@
-mixin printPlan(plan)
-	if (!plan.hideFromUsers)
-		tr(ng-controller="ChangePlanFormController", ng-init="plan="+JSON.stringify(plan))
-			td
-				strong(ng-non-bindable) #{plan.name}
-			td
-				if (plan.annual)
-					| {{displayPrice}} / #{translate("year")}
-				else
-					| {{displayPrice}} / #{translate("month")}
-			td
-				if (typeof(personalSubscription.planCode) != "undefined" && plan.planCode == personalSubscription.planCode.split("_")[0])
-					if (personalSubscription.pendingPlan)
-						form
-							input(type="hidden", ng-model="plan_code", name="plan_code", value=plan.planCode)
-							input(type="submit", ng-click="cancelPendingPlanChange()", value=translate("keep_current_plan")).btn.btn-primary
-					else
-						button.btn.disabled #{translate("your_plan")}
-				else if (personalSubscription.pendingPlan && typeof(personalSubscription.pendingPlan.planCode) != "undefined" && plan.planCode == personalSubscription.pendingPlan.planCode.split("_")[0])
-					button.btn.disabled #{translate("your_new_plan")}
-				else
-					form
-						input(type="hidden", ng-model="plan_code", name="plan_code", value=plan.planCode)
-						input(type="submit", ng-click="changePlan()", value=translate("change_to_this_plan")).btn.btn-primary
-
-mixin printPlans(plans)
-	each plan in plans
-		+printPlan(plan)

+ 0 - 32
services/web/app/views/subscriptions/dashboard/_group_memberships.pug

@@ -1,32 +0,0 @@
-div(ng-controller="GroupMembershipController")
-	each groupSubscription, index in memberGroupSubscriptions
-		unless (groupSubscription.userIsGroupManager)
-			if (user._id+'' != groupSubscription.admin_id._id+'')
-				div
-					p !{translate("you_are_on_x_plan_as_member_of_group_subscription_y_administered_by_z", {planName: groupSubscription.planLevelName, groupName: groupSubscription.teamName || '', adminEmail: groupSubscription.admin_id.email}, [{name: 'a', attrs: {href: '/user/subscription/plans'}}, 'strong'])}
-					if (groupSubscription.teamNotice && groupSubscription.teamNotice != '')
-						p
-							//- Team notice is sanitized in SubscriptionViewModelBuilder
-							em(ng-non-bindable) !{groupSubscription.teamNotice}
-					if index === memberGroupSubscriptions.length - 1
-						include ../_premium_features_link
-					span 
-						button.btn.btn-danger.text-capitalise(ng-click="removeSelfFromGroup('"+groupSubscription._id+"')") #{translate("leave_group")}
-				hr
-
-script(type='text/ng-template', id='LeaveGroupModalTemplate')
-	.modal-header
-		h3 #{translate("leave_group")}
-	.modal-body
-		p #{translate("sure_you_want_to_leave_group")}
-	.modal-footer
-		button.btn.btn-default(
-			ng-disabled="inflight"
-			ng-click="cancel()"
-		) #{translate("cancel")}
-		button.btn.btn-danger(
-			ng-disabled="state.inflight"
-			ng-click="confirmLeaveGroup()"
-		)
-			span(ng-hide="inflight") #{translate("leave_now")}
-			span(ng-show="inflight") #{translate("processing")}…

+ 0 - 10
services/web/app/views/subscriptions/dashboard/_institution_memberships.pug

@@ -1,10 +0,0 @@
-if currentInstitutionsWithLicence === false
-	.alert.alert-warning
-		p Sorry, something went wrong. Subscription information related to institutional affiliations may not be displayed. Please try again later.
-else 
-	each institution, index in currentInstitutionsWithLicence
-		-hasDisplayedSubscription = true
-		p !{translate("you_are_on_x_plan_as_a_confirmed_member_of_institution_y", {planName: 'Professional', institutionName: institution.name || ''}, [{name: 'a', attrs: {href: '/user/subscription/plans'}}, 'strong'])}
-		if (index === currentInstitutionsWithLicence.length - 1)
-			include ../_premium_features_link
-		hr

+ 0 - 24
services/web/app/views/subscriptions/dashboard/_managed_groups.pug

@@ -1,24 +0,0 @@
-each managedGroupSubscription in managedGroupSubscriptions
-	if (managedGroupSubscription.userIsGroupMember)
-		p !{translate("you_are_a_manager_and_member_of_x_plan_as_member_of_group_subscription_y_administered_by_z", {planName: managedGroupSubscription.planLevelName, groupName: managedGroupSubscription.teamName || '', adminEmail: managedGroupSubscription.admin_id.email}, [{name: 'a', attrs: {href: '/user/subscription/plans'}}, 'strong'])}
-	else
-		p !{translate("you_are_a_manager_of_x_plan_as_member_of_group_subscription_y_administered_by_z", {planName: managedGroupSubscription.planLevelName, groupName: managedGroupSubscription.teamName || '', adminEmail: managedGroupSubscription.admin_id.email}, [{name: 'a', attrs: {href: '/user/subscription/plans'}}, 'strong'])}
-	p
-		a.btn.btn-primary(href="/manage/groups/" + managedGroupSubscription._id + "/members")
-			i.fa.fa-fw.fa-users
-			| &nbsp;
-			| Manage members
-		| &nbsp;
-	p
-		a(href="/manage/groups/" + managedGroupSubscription._id + "/managers")
-			i.fa.fa-fw.fa-users
-			| &nbsp;
-			| Manage group managers
-		| &nbsp;
-	p
-		a(href="/metrics/groups/" + managedGroupSubscription._id)
-			i.fa.fa-fw.fa-line-chart
-			| &nbsp;
-			| View metrics
-
-	hr

+ 0 - 24
services/web/app/views/subscriptions/dashboard/_managed_institutions.pug

@@ -1,24 +0,0 @@
-each institution in managedInstitutions
-	p !{translate("you_are_a_manager_of_commons_at_institution_x", {institutionName: institution.name || ''}, ['strong'])}
-	p
-		a.btn.btn-primary(href="/metrics/institutions/" + institution.v1Id)
-			i.fa.fa-fw.fa-line-chart
-			| &nbsp;
-			| View metrics
-	p
-		a(href="/institutions/" + institution.v1Id + "/hub")
-			i.fa.fa-fw.fa-user-circle
-			| &nbsp;
-			| View hub
-	p
-		a(href="/manage/institutions/" + institution.v1Id + "/managers")
-			i.fa.fa-fw.fa-users
-			| &nbsp;
-			| Manage institution managers
-	div(ng-controller="MetricsEmailController", ng-cloak)
-		p
-			span Monthly metrics emails:&nbsp;
-				a(href ng-bind-html="institutionEmailSubscription('"+institution.v1Id+"')" ng-show="!subscriptionChanging" ng-click="changeInstitutionalEmailSubscription('"+institution.v1Id+"')")
-				span(ng-show="subscriptionChanging")
-					i.fa.fa-spin.fa-refresh(aria-hidden="true")
-	hr

+ 0 - 16
services/web/app/views/subscriptions/dashboard/_managed_publishers.pug

@@ -1,16 +0,0 @@
-each publisher in managedPublishers
-	p
-		| You are a manager of
-		|
-		strong(ng-non-bindable)= publisher.name
-	p
-		a(href="/publishers/" + publisher.slug + "/hub")
-			i.fa.fa-fw.fa-user-circle
-			| &nbsp;
-			| View hub
-	p
-		a(href="/manage/publishers/" + publisher.slug + "/managers")
-			i.fa.fa-fw.fa-users
-			| &nbsp;
-			| Manage publisher managers
-	hr

+ 0 - 7
services/web/app/views/subscriptions/dashboard/_personal_subscription.pug

@@ -1,7 +0,0 @@
-if (personalSubscription.recurly)
-	include ./_personal_subscription_recurly
-	include ./_personal_subscription_recurly_sync_email
-else
-	include ./_personal_subscription_custom
-
-hr

+ 0 - 6
services/web/app/views/subscriptions/dashboard/_personal_subscription_custom.pug

@@ -1,6 +0,0 @@
-p
-  | Please
-  |
-  a(href="/contact") contact support
-  |
-  | to make changes to your plan

+ 0 - 159
services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug

@@ -1,159 +0,0 @@
-div(ng-controller="RecurlySubscriptionController")
-	div(ng-show="!showCancellation")
-		if (personalSubscription.recurly.account.has_past_due_invoice && personalSubscription.recurly.account.has_past_due_invoice._ == 'true')
-			.alert.alert-danger #{translate("account_has_past_due_invoice_change_plan_warning")} 
-				| &nbsp;
-				a(href=personalSubscription.recurly.accountManagementLink, target="_blank") #{translate("view_your_invoices")}.
-		case personalSubscription.recurly.state
-			when "active"
-				p !{translate("currently_subscribed_to_plan", {planName: personalSubscription.plan.name}, ['strong'])}
-					if (personalSubscription.pendingPlan)
-						if (personalSubscription.pendingPlan.name != personalSubscription.plan.name)
-							|
-							| !{translate("your_plan_is_changing_at_term_end", {pendingPlanName: personalSubscription.pendingPlan.name}, ['strong'])}
-						if (personalSubscription.recurly.pendingAdditionalLicenses > 0 || personalSubscription.recurly.additionalLicenses > 0)
-							|
-							| !{translate("pending_additional_licenses", {pendingAdditionalLicenses: personalSubscription.recurly.pendingAdditionalLicenses, pendingTotalLicenses: personalSubscription.recurly.pendingTotalLicenses}, ['strong', 'strong'])}
-					else if (personalSubscription.recurly.additionalLicenses > 0)
-						|
-						| !{translate("additional_licenses", {additionalLicenses: personalSubscription.recurly.additionalLicenses, totalLicenses: personalSubscription.recurly.totalLicenses}, ['strong', 'strong'])}
-					| &nbsp;
-					a(href, ng-click="switchToChangePlanView()", ng-if="showChangePlanButton") !{translate("change_plan")}.
-				if (personalSubscription.pendingPlan && personalSubscription.pendingPlan.name != personalSubscription.plan.name)
-					p #{translate("want_change_to_apply_before_plan_end")}
-				else if (personalSubscription.plan.groupPlan)
-					p !{translate("contact_support_to_change_group_subscription", {}, [{ name: "a", attrs: { href: "/contact"}}])}
-				if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now())
-					p You're on a free trial which ends on <strong ng-non-bindable>#{personalSubscription.recurly.trialEndsAtFormatted}</strong>
-				p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount: personalSubscription.recurly.displayPrice, collectionDate: personalSubscription.recurly.nextPaymentDueAt}, ['strong', 'strong'])}
-				include ../_premium_features_link
-				include ./../_price_exceptions
-				p.pull-right
-				p
-					a(href=personalSubscription.recurly.billingDetailsLink, target="_blank").btn.btn-secondary-info.btn-secondary #{translate("update_your_billing_details")}
-					| &nbsp;
-					a(href=personalSubscription.recurly.accountManagementLink, target="_blank").btn.btn-secondary-info.btn-secondary #{translate("view_your_invoices")}
-					| &nbsp;
-					unless (cancelButtonNewCopy)
-						a(href, ng-click="switchToCancellationView()", ng-hide="recurlyLoadError", event-tracking='subscription-page-cancel-button-click', event-tracking-mb="true", event-tracking-trigger="click").btn.btn-danger !{translate("stop_your_subscription")}
-				if (cancelButtonNewCopy)
-					p
-						a(href, ng-click="switchToCancellationView()", ng-hide="recurlyLoadError", event-tracking='subscription-page-cancel-button-click', event-tracking-mb="true", event-tracking-trigger="click").btn.btn-danger !{translate("cancel_your_subscription")}
-					unless (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now())
-						p
-							i !{translate("subscription_will_remain_active_until_end_of_billing_period_x", {terminationDate: personalSubscription.recurly.nextPaymentDueAt}, ['strong'])}
-			when "canceled"
-				p !{translate("currently_subscribed_to_plan", {planName: personalSubscription.plan.name}, ['strong'])}
-				p !{translate("subscription_canceled_and_terminate_on_x", {terminateDate: personalSubscription.recurly.nextPaymentDueAt}, ['strong'])}
-				include ../_premium_features_link
-				p
-					a(href=personalSubscription.recurly.accountManagementLink, target="_blank").btn.btn-secondary-info.btn-secondary #{translate("view_your_invoices")}
-					p: form(action="/user/subscription/reactivate",method="post")
-						input(type="hidden", name="_csrf", value=csrfToken)
-						input(type="submit",value="Reactivate your subscription").btn.btn-primary
-			when "expired"
-				p !{translate("your_subscription_has_expired")}
-				p
-					a(href=personalSubscription.recurly.accountManagementLink, target="_blank").btn.btn-secondary-info.btn-secondary #{translate("view_your_invoices")}
-					| &nbsp;
-					a(href="/user/subscription/plans").btn.btn-primary !{translate("create_new_subscription")}
-			default
-				p !{translate("problem_with_subscription_contact_us")}
-
-		.alert.alert-warning(ng-show="recurlyLoadError")
-			strong #{translate('payment_provider_unreachable_error')}
-
-	include ./_change_plans_mixins
-	div(ng-show="showChangePlan", ng-cloak)
-		h2 !{translate("change_plan")}
-		p: table.table
-			tr
-				th !{translate("name")}
-				th !{translate("price")}
-				th
-			+printPlans(plans.studentAccounts)
-			+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-primary(
-					href="#groups"
-					ng-click="openGroupPlanModal()"
-				) #{translate('change_to_group_plan')}
-			div(ng-hide="isValidCurrencyForUpgrade")
-				span !{translate('contact_support_to_upgrade_to_group_subscription', {}, [{ name: "a", attrs: { href: "/contact"}}])}
-
-
-	.div(ng-controller="RecurlyCancellationController", ng-show="showCancellation").text-center
-		p
-			strong #{translate("wed_love_you_to_stay")}
-
-		div(ng-show="showExtendFreeTrial")
-			p !{translate("have_more_days_to_try", {days:14})}
-			p
-				button(type="submit", ng-click="extendTrial()", ng-disabled='inflight').btn.btn-primary #{translate("ill_take_it")}
-			p
-				a(href, ng-click="cancelSubscription()", ng-disabled='inflight') #{translate("no_thanks_cancel_now")}
-
-		div(ng-show="showDowngrade")
-			div(ng-controller="ChangePlanFormController")
-				p !{translate("interested_in_cheaper_personal_plan", {price:'{{personalDisplayPrice}}'}, ['strong'] )}
-				p
-					button(type="submit", ng-click="downgradeToPaidPersonal()", ng-disabled='inflight').btn.btn-primary #{translate("yes_move_me_to_personal_plan")}
-				p
-					a(href, ng-click="cancelSubscription()", ng-disabled='inflight')  #{translate("no_thanks_cancel_now")}
-
-		div(ng-show="showBasicCancel")
-			p
-				a(href, ng-click="switchToDefaultView()").btn.btn-secondary-info.btn-secondary #{translate("i_want_to_stay")}
-			p
-				a(href, ng-click="cancelSubscription()", ng-disabled='inflight').btn.btn-primary #{translate("cancel_my_account")}
-
-script(type='text/ng-template', id='confirmChangePlanModalTemplate')
-	.modal-header
-		h3 #{translate("change_plan")}
-	.modal-body
-		.alert.alert-warning(ng-show="genericError")
-			strong #{translate("generic_something_went_wrong")}. #{translate("try_again")}. #{translate("generic_if_problem_continues_contact_us")}.
-		p !{translate("sure_you_want_to_change_plan", {planName: '{{plan.name}}'}, ['strong'])}
-		div(ng-show="planChangesAtTermEnd")
-			p #{translate("existing_plan_active_until_term_end")}
-			p #{translate("want_change_to_apply_before_plan_end")}
-	.modal-footer
-		button.btn.btn-default(
-			ng-disabled="inflight"
-			ng-click="cancel()"
-		) #{translate("cancel")}
-		button.btn.btn-primary(
-			ng-disabled="inflight"
-			ng-click="confirmChangePlan()"
-		)
-			span(ng-hide="inflight") #{translate("change_plan")}
-			span(ng-show="inflight") #{translate("processing")}…
-
-script(type='text/ng-template', id='cancelPendingPlanChangeModalTemplate')
-	.modal-header
-		h3 #{translate("change_plan")}
-	.modal-body
-		.alert.alert-warning(ng-show="genericError")
-			strong #{translate("generic_something_went_wrong")}. #{translate("try_again")}. #{translate("generic_if_problem_continues_contact_us")}.
-		p !{translate("sure_you_want_to_cancel_plan_change", {planName: '{{plan.name}}'}, ['strong'])}
-	.modal-footer
-		button.btn.btn-default(
-			ng-disabled="inflight"
-			ng-click="cancel()"
-		) #{translate("cancel")}
-		button.btn.btn-primary(
-			ng-disabled="inflight"
-			ng-click="confirmCancelPendingPlanChange()"
-		)
-			span(ng-hide="inflight") #{translate("revert_pending_plan_change")}
-			span(ng-show="inflight") #{translate("processing")}…
-
-include ../_plans_page_mixins
-include ../_modal_group_upgrade

+ 0 - 18
services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly_sync_email.pug

@@ -1,18 +0,0 @@
--if (user.email !== personalSubscription.recurly.account.email)
-	div
-		hr
-		form(async-form="updateAccountEmailAddress", name="updateAccountEmailAddress", action='/user/subscription/account/email', method="POST")
-			input(name='_csrf', type='hidden', value=csrfToken)
-			.form-group
-				form-messages(for="updateAccountEmailAddress")
-					.alert.alert-success(ng-show="updateAccountEmailAddress.response.success")
-						| #{translate('recurly_email_updated')}
-				div(ng-hide="updateAccountEmailAddress.response.success")
-					p(ng-non-bindable) !{translate("recurly_email_update_needed", { recurlyEmail: personalSubscription.recurly.account.email, userEmail: user.email }, ['em', 'em'])}
-					.actions
-						button.btn-primary.btn(
-							type='submit',
-							ng-disabled="updateAccountEmailAddress.inflight"
-						)
-							span(ng-show="!updateAccountEmailAddress.inflight") #{translate("update")}
-							span(ng-show="updateAccountEmailAddress.inflight") #{translate("updating")}…

+ 0 - 9
services/web/app/views/subscriptions/dashboard/_team_name_mixin.pug

@@ -1,9 +0,0 @@
-mixin teamName(subscription)
-	if (subscription.teamName && subscription.teamName != '')
-		strong(ng-non-bindable)= subscription.teamName
-	else if (subscription.admin_id._id == user._id)
-		| a group account
-	else
-		| the group account owned by
-		|
-		strong= subscription.admin_id.email

+ 0 - 62
services/web/app/views/subscriptions/dashboard/_v1_subscription_status.pug

@@ -1,62 +0,0 @@
-if (v1SubscriptionStatus['team'] && v1SubscriptionStatus['team']['default_plan_name'] != 'free')
-  - hasDisplayedSubscription = true
-  p
-    | You have a legacy group licence from Overleaf v1.
-  if (v1SubscriptionStatus['team']['will_end_at'])
-    p
-      | Your current group licence ends on
-      |
-      strong= moment(v1SubscriptionStatus['team']['will_end_at']).format('Do MMM YY')
-      |
-      | and will
-      |
-      if (v1SubscriptionStatus['team']['will_renew'])
-        | be automatically renewed.
-      else
-        | not be automatically renewed.
-  if (v1SubscriptionStatus['can_cancel_team'])
-    p
-      form(method="POST", action="/user/subscription/v1/cancel")
-        input(type="hidden", name="_csrf", value=csrfToken)
-        button().btn.btn-danger Stop automatic renewal
-  else
-    p
-      | Please
-      |
-      a(href="/contact") contact support
-      |
-      | to make changes to your plan
-  hr
-
-if (v1SubscriptionStatus['product'])
-  - hasDisplayedSubscription = true
-  p
-    | You have a legacy Overleaf v1
-    |
-    strong= v1SubscriptionStatus['product']['display_name']
-    |
-    | plan.
-  p
-    | Your plan ends on
-    |
-    strong= moment(v1SubscriptionStatus['product']['will_end_at']).format('Do MMM YY')
-    |
-    | and will
-    |
-    if (v1SubscriptionStatus['product']['will_renew'])
-      | be automatically renewed.
-    else
-      | not be automatically renewed.
-  if (v1SubscriptionStatus['can_cancel'])
-    p
-      form(method="POST", action="/user/subscription/v1/cancel")
-        input(type="hidden", name="_csrf", value=csrfToken)
-        button().btn.btn-danger Stop automatic renewal
-  else
-    p
-      | Please
-      |
-      a(href="/contact") contact support
-      |
-      | to make changes to your plan
-  hr

+ 0 - 364
services/web/app/views/subscriptions/new-refreshed.pug

@@ -1,364 +0,0 @@
-extends ../layout
-
-include ./_new_mixins
-
-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.symbolPalette
-										li #{translate("symbol_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
-											| &ndash;{{ 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")}
-
-				.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
-																	| &nbsp;
-																	i.fa.fa-cc-visa(aria-hidden="true")
-																	| &nbsp;
-																	i.fa.fa-cc-mastercard(aria-hidden="true")
-																	| &nbsp;
-																	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
-																	| &nbsp;
-																	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')} &nbsp;
-															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-primary.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')}
-														| &nbsp;
-													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'])}
-
-	+studentCheckModal

+ 0 - 40
services/web/app/views/subscriptions/successful-subscription.pug

@@ -1,40 +0,0 @@
-extends ../layout
-
-block content
-	- var featuresPageVariant= splitTestVariants && splitTestVariants['features-page'] ? splitTestVariants['features-page'] : 'default'
-	- var featuresLink = featuresPageVariant === 'new' ? "/about/features-overview" : "/learn/how-to/Overleaf_premium_features"
-	- var featuresTranslationKey = featuresPageVariant === 'new' ? 'get_most_subscription_by_checking_features' : 'get_most_subscription_by_checking_premium_features'
-	- var featuresLinkSegmentation = {splitTest: 'features-page', splitTestVariant: featuresPageVariant}
-
-	main.content.content-alt#main-content
-		.container
-			.row
-				.col-md-8.col-md-offset-2
-					.card(ng-cloak)
-						.page-header
-							h2 #{translate("thanks_for_subscribing")}
-
-						.alert.alert-success
-							if (personalSubscription.recurly.trial_ends_at)
-								p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount: personalSubscription.recurly.displayPrice, collectionDate: personalSubscription.recurly.nextPaymentDueAt}, ['strong', 'strong'])}
-								include ./_price_exceptions
-							p #{translate("to_modify_your_subscription_go_to")} 
-								a(href="/user/subscription")  #{translate("manage_subscription")}.
-						p
-							if (personalSubscription.groupPlan == true)
-								a.btn.btn-primary.btn-large(href=`/manage/groups/${personalSubscription._id}/members`) #{translate("add_your_first_group_member_now")}
-						p.letter-from-founders
-							p #{translate("thanks_for_subscribing_you_help_sl", {planName:personalSubscription.plan.name})}
-							p !{translate(featuresTranslationKey, {}, [{name: 'a', attrs: {href: featuresLink, 'event-tracking':'features-page-link', 'event-tracking-trigger': 'click', 'event-tracking-mb': 'true', 'event-segmentation': featuresLinkSegmentation}}])}
-							p #{translate("need_anything_contact_us_at")} 
-								a(href=`mailto:${settings.adminEmail}`, ng-non-bindable) #{settings.adminEmail}
-								| .
-							p !{translate("help_improve_overleaf_fill_out_this_survey", {}, [{name: 'a', attrs: {href: 'https://forms.gle/CdLNX9m6NLxkv1yr5', target:'_blank', rel:'noopener noreferrer'}}])}
-							p #{translate("regards")},
-								br(ng-non-bindable)
-								| The #{settings.appName} Team
-						p
-							if (postCheckoutRedirect)
-								a.btn.btn-primary(href=postCheckoutRedirect) &lt; #{translate("back_to_your_projects")}
-							else
-								a.btn.btn-primary(href="/project") &lt; #{translate("back_to_your_projects")}

+ 0 - 119
services/web/app/views/user_membership/index.pug

@@ -1,119 +0,0 @@
-extends ../layout
-
-block append meta
-	meta(name="ol-users", data-type="json", content=users)
-	meta(name="ol-paths", data-type="json", content=paths)
-	meta(name="ol-groupSize", data-type="json", content=groupSize)
-
-block content
-	main.content.content-alt#main-content
-		.container
-			.row
-				.col-md-10.col-md-offset-1
-					h1(ng-non-bindable) #{name || translate(translations.title)}
-					.card(ng-controller="UserMembershipController")
-						.page-header
-							.pull-right(ng-cloak)
-								small(ng-show="groupSize && selectedUsers.length == 0") !{translate("you_have_added_x_of_group_size_y", {addedUsersSize:'{{ users.length }}', groupSize: '{{ groupSize }}'}, ['strong', 'strong'])}
-								a.btn.btn-danger(
-									href,
-									ng-show="selectedUsers.length > 0"
-									ng-click="removeMembers()"
-								) #{translate(translations.remove)}
-							h3 #{translate(translations.subtitle)}
-
-						.row-spaced-small
-							div(ng-if="inputs.removeMembers.error", ng-cloak)
-								div.alert.alert-danger(ng-if="inputs.removeMembers.errorMessage")
-									| #{translate('error')}:
-									| {{ inputs.removeMembers.errorMessage }}
-								div.alert.alert-danger(ng-if="!inputs.removeMembers.errorMessage")
-									| #{translate('generic_something_went_wrong')}
-							ul.list-unstyled.structured-list(
-								select-all-list,
-								ng-cloak
-							)
-								li.container-fluid
-									.row
-										.col-md-4
-											input.select-all(
-												select-all,
-												type="checkbox"
-											)
-											span.header #{translate("email")}
-										.col-md-4
-											span.header #{translate("name")}
-										.col-md-2
-											span.header(
-												tooltip=translate('last_active_description')
-												tooltip-placement="left"
-												tooltip-append-to-body="true"
-											)
-												| #{translate("last_active")}
-												sup (?)
-										.col-md-2
-											span.header #{translate("accepted_invite")}
-								li.container-fluid(
-									ng-repeat="user in users | orderBy:'email':true",
-									ng-controller="UserMembershipListItemController"
-								)
-									.row
-										.col-md-4
-											input.select-item(
-												select-individual,
-												type="checkbox",
-												ng-model="user.selected"
-											)
-											span.email {{ user.email }}
-										.col-md-4
-											span.name {{ user.first_name }} {{ user.last_name }}
-										.col-md-2
-											span.lastLogin {{ user.last_active_at | formatDate:'Do MMM YYYY' }}
-										.col-md-2
-											span.registered
-												i.fa.fa-check.text-success(ng-show="!user.invite" aria-hidden="true")
-												span.sr-only(ng-show="!user.invite") #{translate('accepted_invite')}
-												i.fa.fa-times(ng-show="user.invite" aria-hidden="true")
-												span.sr-only(ng-show="user.invite") #{translate('invite_not_accepted')}
-								li(
-									ng-if="users.length == 0",
-									ng-cloak
-								)
-									.row
-										.col-md-12.text-centered
-											small #{translate("no_members")}
-
-						hr
-						div(ng-if="!groupSize || users.length < groupSize", ng-cloak)
-							p.small #{translate("add_more_members")}
-							div(ng-if="inputs.addMembers.error", ng-cloak)
-								div.alert.alert-danger(ng-if="inputs.addMembers.errorMessage")
-									| #{translate('error')}:
-									| {{ inputs.addMembers.errorMessage }}
-								div.alert.alert-danger(ng-if="!inputs.addMembers.errorMessage")
-									| #{translate('generic_something_went_wrong')}
-							form.form
-								.row
-									.col-xs-6
-										input.form-control(
-											name="email",
-											type="text",
-											placeholder="jane@example.com, joe@example.com",
-											ng-model="inputs.addMembers.content",
-											on-enter="addMembers()"
-											aria-describedby="add-members-description"
-										)
-									.col-xs-4
-										button.btn.btn-primary(ng-click="addMembers()", ng-disabled="inputs.addMembers.inflightCount > 0")
-											span(ng-show="inputs.addMembers.inflightCount === 0") #{translate("add")}
-											span(ng-show="inputs.addMembers.inflightCount > 0") #{translate("adding")}…
-									.col-xs-2(ng-if="paths.exportMembers", ng-cloak)
-										a(href=paths.exportMembers) #{translate('export_csv')}
-								.row
-									.col-xs-8
-										span.help-block #{translate('add_comma_separated_emails_help')}
-
-						div(ng-if="groupSize && users.length >= groupSize && users.length > 0", ng-cloak)
-							.row
-								.col-xs-2.col-xs-offset-10(ng-if="paths.exportMembers", ng-cloak)
-									a(href=paths.exportMembers) #{translate('export_csv')}

+ 0 - 4
services/web/frontend/js/main.js

@@ -14,17 +14,13 @@ import './main/account-settings'
 import './main/clear-sessions'
 import './main/clear-sessions'
 import './main/account-upgrade-angular'
 import './main/account-upgrade-angular'
 import './main/plans'
 import './main/plans'
-import './main/user-membership'
 import './main/scribtex-popup'
 import './main/scribtex-popup'
 import './main/event'
 import './main/event'
 import './main/bonus'
 import './main/bonus'
 import './main/system-messages'
 import './main/system-messages'
 import './main/translations'
 import './main/translations'
-import './main/subscription-dashboard'
-import './main/new-subscription'
 import './main/annual-upgrade'
 import './main/annual-upgrade'
 import './main/subscription/team-invite-controller'
 import './main/subscription/team-invite-controller'
-import './main/subscription/upgrade-subscription'
 import './main/learn'
 import './main/learn'
 import './main/affiliations/components/inputSuggestions'
 import './main/affiliations/components/inputSuggestions'
 import './main/affiliations/factories/UserAffiliationsDataService'
 import './main/affiliations/factories/UserAffiliationsDataService'

+ 0 - 793
services/web/frontend/js/main/new-subscription.js

@@ -1,793 +0,0 @@
-import _ from 'lodash'
-/* eslint-disable
-    camelcase,
-    max-len,
-    no-return-assign
-*/
-/* global recurly */
-import App from '../base'
-import getMeta from '../utils/meta'
-import { assign } from '../shared/components/location'
-
-export default App.controller(
-  'NewSubscriptionController',
-  function (
-    $scope,
-    $modal,
-    MultiCurrencyPricing,
-    $http,
-    $location,
-    eventTracking
-  ) {
-    window.couponCode = $location.search().cc || ''
-    window.plan_code = $location.search().planCode || ''
-    window.ITMCampaign = $location.search().itm_campaign || ''
-    window.ITMContent = $location.search().itm_content || ''
-    window.ITMReferrer = $location.search().itm_referrer || ''
-
-    if (typeof recurly === 'undefined' || !recurly) {
-      $scope.recurlyLoadError = true
-      return
-    }
-
-    $scope.ui = {
-      showCurrencyDropdown: false,
-      showAddressSecondLine: false,
-      addCompanyDetails: false,
-    }
-
-    $scope.recurlyLoadError = false
-    $scope.currencyCode = MultiCurrencyPricing.currencyCode
-    $scope.initiallySelectedCurrencyCode = MultiCurrencyPricing.currencyCode
-    $scope.allCurrencies = MultiCurrencyPricing.plans
-    $scope.availableCurrencies = {}
-    $scope.planCode = window.plan_code
-
-    const isStudentCheckModalEnabled =
-      getMeta('ol-splitTestVariants')?.['student-check-modal'] === 'enabled'
-
-    if (isStudentCheckModalEnabled && $scope.planCode.includes('student')) {
-      $modal.open({
-        templateUrl: 'StudentCheckModalTemplate',
-        controller: 'StudentCheckModalController',
-        backdrop: 'static',
-        size: 'dialog-centered',
-      })
-    }
-
-    $scope.switchToStudent = function () {
-      const currentPlanCode = window.plan_code
-      const planCode = currentPlanCode.replace('collaborator', 'student')
-      eventTracking.sendMB('payment-page-switch-to-student', {
-        plan_code: window.plan_code,
-      })
-      eventTracking.send(
-        'subscription-funnel',
-        'subscription-form-switch-to-student',
-        window.plan_code
-      )
-      window.location =
-        '/user/subscription/new' +
-        `?planCode=${planCode}` +
-        `&currency=${$scope.currencyCode}` +
-        `&cc=${$scope.data.coupon}` +
-        `&itm_campaign=${window.ITMCampaign}` +
-        `&itm_content=${window.ITMContent}` +
-        `&itm_referrer=${window.ITMReferrer}`
-    }
-
-    eventTracking.sendMB('payment-page-view', {
-      plan: window.plan_code,
-      currency: $scope.currencyCode,
-    })
-    eventTracking.send(
-      'subscription-funnel',
-      'subscription-form-viewed',
-      window.plan_code
-    )
-
-    $scope.paymentMethod = { value: 'credit_card' }
-
-    $scope.data = {
-      first_name: '',
-      last_name: '',
-      postal_code: '',
-      address1: '',
-      address2: '',
-      state: '',
-      city: '',
-      company: '',
-      vat_number: '',
-      country: getMeta('ol-countryCode'),
-      coupon: window.couponCode,
-    }
-
-    $scope.validation = {}
-
-    $scope.processing = false
-
-    $scope.threeDSecureFlow = false
-    $scope.threeDSecureContainer = document.querySelector(
-      '.three-d-secure-container'
-    )
-    $scope.threeDSecureRecurlyContainer = document.querySelector(
-      '.three-d-secure-recurly-container'
-    )
-
-    recurly.configure({
-      publicKey: getMeta('ol-recurlyApiKey'),
-      style: {
-        all: {
-          fontFamily: '"Open Sans", sans-serif',
-          fontSize: '16px',
-          fontColor: '#7a7a7a',
-        },
-        month: {
-          placeholder: 'MM',
-        },
-        year: {
-          placeholder: 'YY',
-        },
-        cvv: {
-          placeholder: 'CVV',
-        },
-      },
-    })
-
-    const pricing = recurly.Pricing()
-    window.pricing = pricing
-
-    function setupPricing() {
-      pricing
-        .plan(window.plan_code, { quantity: 1 })
-        .address({
-          country: $scope.data.country,
-        })
-        .tax({ tax_code: 'digital', vat_number: '' })
-        .currency($scope.currencyCode)
-        .coupon($scope.data.coupon)
-        .catch(function (err) {
-          if (
-            $scope.currencyCode !== 'USD' &&
-            err.name === 'invalid-currency'
-          ) {
-            $scope.currencyCode = 'USD'
-            setupPricing()
-          } else if (err.name === 'api-error' && err.code === 'not-found') {
-            // not-found here should refer to the coupon code, plan_code should be valid
-            $scope.$applyAsync(() => {
-              $scope.couponError = 'Coupon code is not valid for selected plan'
-            })
-          } else {
-            // Bail out on other errors, form state will not be correct
-            $scope.$applyAsync(() => {
-              $scope.recurlyLoadError = true
-            })
-            throw err
-          }
-        })
-        .done()
-    }
-
-    setupPricing()
-
-    pricing.on('change', () => {
-      $scope.planName = pricing.items.plan.name
-
-      if (pricing.items.plan.trial) {
-        $scope.trialLength = pricing.items.plan.trial.length
-      }
-
-      $scope.recurlyPrice = $scope.trialLength
-        ? pricing.price.next
-        : pricing.price.now
-      $scope.taxes = pricing.price.taxes
-      $scope.monthlyBilling = pricing.items.plan.period.length === 1
-
-      $scope.availableCurrencies = {}
-      for (const currencyCode in pricing.items.plan.price) {
-        if (MultiCurrencyPricing.plans[currencyCode]) {
-          $scope.availableCurrencies[currencyCode] =
-            MultiCurrencyPricing.plans[currencyCode]
-        }
-      }
-
-      $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 &&
-        pricing.items.coupon.discount &&
-        pricing.items.coupon.discount.type === 'percent'
-      ) {
-        const basePrice = parseInt(pricing.price.base.plan.unit, 10)
-        $scope.coupon = {
-          singleUse: pricing.items.coupon.single_use,
-          normalPrice: basePrice,
-          name: pricing.items.coupon.name,
-          normalPriceWithoutTax: basePrice,
-        }
-        if (
-          pricing.items.coupon.applies_for_months > 0 &&
-          pricing.items.coupon.discount.rate &&
-          pricing.items.coupon.applies_for_months
-        ) {
-          $scope.coupon.discountMonths = pricing.items.coupon.applies_for_months
-          $scope.coupon.discountRate = pricing.items.coupon.discount.rate * 100
-        }
-
-        if (pricing.price.taxes[0] && pricing.price.taxes[0].rate) {
-          $scope.coupon.normalPrice += basePrice * pricing.price.taxes[0].rate
-        }
-      } else {
-        $scope.coupon = null
-      }
-      $scope.$apply()
-    })
-
-    $scope.applyCoupon = () => {
-      $scope.couponError = ''
-      pricing
-        .coupon($scope.data.coupon)
-        .catch(err => {
-          if (err.name === 'api-error' && err.code === 'not-found') {
-            $scope.$applyAsync(() => {
-              $scope.couponError = 'Coupon code is not valid for selected plan'
-            })
-          } else {
-            $scope.$applyAsync(() => {
-              $scope.couponError =
-                'An error occured when verifying the coupon code'
-            })
-            throw err
-          }
-        })
-        .done()
-    }
-
-    $scope.showAddressSecondLine = function (e) {
-      e.preventDefault()
-      $scope.ui.showAddressSecondLine = true
-    }
-
-    $scope.showCurrencyDropdown = function (e) {
-      e.preventDefault()
-      $scope.ui.showCurrencyDropdown = true
-    }
-
-    const elements = recurly.Elements()
-    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 })
-        .done()
-
-    $scope.changeCurrency = function (newCurrency) {
-      $scope.currencyCode = newCurrency
-      return pricing
-        .currency(newCurrency)
-        .catch(function (err) {
-          if (
-            $scope.currencyCode !== 'USD' &&
-            err.name === 'invalid-currency'
-          ) {
-            $scope.changeCurrency('USD')
-          } else {
-            throw err
-          }
-        })
-        .done()
-    }
-
-    $scope.inputHasError = function (formItem) {
-      if (formItem == null) {
-        return false
-      }
-
-      return formItem.$touched && formItem.$invalid
-    }
-
-    $scope.isFormValid = function (form) {
-      if ($scope.paymentMethod.value === 'paypal') {
-        return $scope.data.country !== ''
-      } else {
-        return form.$valid && $scope.cardIsValid
-      }
-    }
-
-    $scope.updateCountry = () =>
-      pricing.address({ country: $scope.data.country }).done()
-
-    $scope.setPaymentMethod = function (method) {
-      $scope.paymentMethod.value = method
-      $scope.validation.errorFields = {}
-      $scope.genericError = ''
-    }
-
-    let cachedRecurlyBillingToken
-    const completeSubscription = function (
-      err,
-      recurlyBillingToken,
-      recurly3DSecureResultToken
-    ) {
-      if (recurlyBillingToken) {
-        // temporary store the billing token as it might be needed when
-        // re-sending the request after SCA authentication
-        cachedRecurlyBillingToken = recurlyBillingToken
-      }
-      $scope.validation.errorFields = {}
-      if (err != null) {
-        eventTracking.sendMB('payment-page-form-error', err)
-        eventTracking.send('subscription-funnel', 'subscription-error')
-        // We may or may not be in a digest loop here depending on
-        // whether recurly could do validation locally, so do it async
-        $scope.$evalAsync(function () {
-          $scope.processing = false
-          $scope.genericError = err.message
-          _.each(
-            err.fields,
-            field => ($scope.validation.errorFields[field] = true)
-          )
-        })
-      } else {
-        const postData = {
-          _csrf: window.csrfToken,
-          recurly_token_id: cachedRecurlyBillingToken.id,
-          recurly_three_d_secure_action_result_token_id:
-            recurly3DSecureResultToken && recurly3DSecureResultToken.id,
-          subscriptionDetails: {
-            currencyCode: pricing.items.currency,
-            plan_code: pricing.items.plan.code,
-            coupon_code: pricing.items.coupon ? pricing.items.coupon.code : '',
-            first_name: $scope.data.first_name,
-            last_name: $scope.data.last_name,
-            isPaypal: $scope.paymentMethod.value === 'paypal',
-            address: {
-              address1: $scope.data.address1,
-              address2: $scope.data.address2,
-              country: $scope.data.country,
-              state: $scope.data.state,
-              zip: $scope.data.postal_code,
-            },
-            ITMCampaign: window.ITMCampaign,
-            ITMContent: window.ITMContent,
-            ITMReferrer: window.ITMReferrer,
-          },
-        }
-
-        if (
-          postData.subscriptionDetails.isPaypal &&
-          $scope.ui.addCompanyDetails
-        ) {
-          postData.subscriptionDetails.billing_info = {}
-          if ($scope.data.company && $scope.data.company !== '') {
-            postData.subscriptionDetails.billing_info.company =
-              $scope.data.company
-          }
-          if ($scope.data.vat_number && $scope.data.vat_number !== '') {
-            postData.subscriptionDetails.billing_info.vat_number =
-              $scope.data.vat_number
-          }
-        }
-
-        eventTracking.sendMB('payment-page-form-submit', {
-          currencyCode: postData.subscriptionDetails.currencyCode,
-          plan_code: postData.subscriptionDetails.plan_code,
-          coupon_code: postData.subscriptionDetails.coupon_code,
-          isPaypal: postData.subscriptionDetails.isPaypal,
-        })
-        eventTracking.send(
-          'subscription-funnel',
-          'subscription-form-submitted',
-          postData.subscriptionDetails.plan_code
-        )
-        return $http
-          .post('/user/subscription/create', postData)
-          .then(function () {
-            eventTracking.sendMB('payment-page-form-success')
-            eventTracking.send(
-              'subscription-funnel',
-              'subscription-submission-success',
-              postData.subscriptionDetails.plan_code
-            )
-            window.location.href = '/user/subscription/thank-you'
-          })
-          .catch(response => {
-            $scope.processing = false
-            const { data } = response
-            $scope.genericError =
-              (data && data.message) ||
-              'Something went wrong processing the request'
-
-            if (data.threeDSecureActionTokenId) {
-              initThreeDSecure(data.threeDSecureActionTokenId)
-            }
-          })
-      }
-    }
-
-    $scope.submit = function () {
-      $scope.processing = true
-      if ($scope.paymentMethod.value === 'paypal') {
-        const opts = { description: $scope.planName }
-        recurly.paypal(opts, completeSubscription)
-      } else {
-        const tokenData = _.cloneDeep($scope.data)
-        if (!$scope.ui.addCompanyDetails) {
-          delete tokenData.company
-          delete tokenData.vat_number
-        }
-        recurly.token(elements, tokenData, completeSubscription)
-      }
-    }
-
-    const initThreeDSecure = function (threeDSecureActionTokenId) {
-      // instanciate and configure Recurly 3DSecure flow
-      const risk = recurly.Risk()
-      const threeDSecure = risk.ThreeDSecure({
-        actionTokenId: threeDSecureActionTokenId,
-      })
-
-      // on SCA verification error: show payment UI with the error message
-      threeDSecure.on('error', error => {
-        $scope.genericError = `Error: ${error.message}`
-        $scope.threeDSecureFlow = false
-        $scope.$apply()
-      })
-
-      // on SCA verification success: show payment UI in processing mode and
-      // resubmit the payment with the new token final success or error will be
-      // handled by `completeSubscription`
-      threeDSecure.on('token', recurly3DSecureResultToken => {
-        completeSubscription(null, null, recurly3DSecureResultToken)
-        $scope.genericError = null
-        $scope.threeDSecureFlow = false
-        $scope.processing = true
-        $scope.$apply()
-      })
-
-      // make sure the threeDSecureRecurlyContainer is empty (in case of
-      // retries) and show 3DSecure UI
-      $scope.threeDSecureRecurlyContainer.innerHTML = ''
-      $scope.threeDSecureFlow = true
-      threeDSecure.attach($scope.threeDSecureRecurlyContainer)
-
-      // scroll the UI into view (timeout needed to make sure the element is
-      // visible)
-      window.setTimeout(() => {
-        $scope.threeDSecureContainer.scrollIntoView()
-      }, 0)
-    }
-
-    // list taken from Recurly (see https://docs.recurly.com/docs/countries-provinces-and-states). Country code must exist on Recurly, so update with care
-    $scope.countries = [
-      { code: 'AF', name: 'Afghanistan' },
-      { code: 'AX', name: 'Åland Islands' },
-      { code: 'AL', name: 'Albania' },
-      { code: 'DZ', name: 'Algeria' },
-      { code: 'AS', name: 'American Samoa' },
-      { code: 'AD', name: 'Andorra' },
-      { code: 'AO', name: 'Angola' },
-      { code: 'AI', name: 'Anguilla' },
-      { code: 'AQ', name: 'Antarctica' },
-      { code: 'AG', name: 'Antigua and Barbuda' },
-      { code: 'AR', name: 'Argentina' },
-      { code: 'AM', name: 'Armenia' },
-      { code: 'AW', name: 'Aruba' },
-      { code: 'AC', name: 'Ascension Island' },
-      { code: 'AU', name: 'Australia' },
-      { code: 'AT', name: 'Austria' },
-      { code: 'AZ', name: 'Azerbaijan' },
-      { code: 'BS', name: 'Bahamas' },
-      { code: 'BH', name: 'Bahrain' },
-      { code: 'BD', name: 'Bangladesh' },
-      { code: 'BB', name: 'Barbados' },
-      { code: 'BY', name: 'Belarus' },
-      { code: 'BE', name: 'Belgium' },
-      { code: 'BZ', name: 'Belize' },
-      { code: 'BJ', name: 'Benin' },
-      { code: 'BM', name: 'Bermuda' },
-      { code: 'BT', name: 'Bhutan' },
-      { code: 'BO', name: 'Bolivia' },
-      { code: 'BA', name: 'Bosnia and Herzegovina' },
-      { code: 'BW', name: 'Botswana' },
-      { code: 'BV', name: 'Bouvet Island' },
-      { code: 'BR', name: 'Brazil' },
-      { code: 'BQ', name: 'British Antarctic Territory' },
-      { code: 'IO', name: 'British Indian Ocean Territory' },
-      { code: 'VG', name: 'British Virgin Islands' },
-      { code: 'BN', name: 'Brunei' },
-      { code: 'BG', name: 'Bulgaria' },
-      { code: 'BF', name: 'Burkina Faso' },
-      { code: 'BI', name: 'Burundi' },
-      { code: 'CV', name: 'Cabo Verde' },
-      { code: 'KH', name: 'Cambodia' },
-      { code: 'CM', name: 'Cameroon' },
-      { code: 'CA', name: 'Canada' },
-      { code: 'IC', name: 'Canary Islands' },
-      { code: 'CT', name: 'Canton and Enderbury Islands' },
-      { code: 'KY', name: 'Cayman Islands' },
-      { code: 'CF', name: 'Central African Republic' },
-      { code: 'EA', name: 'Ceuta and Melilla' },
-      { code: 'TD', name: 'Chad' },
-      { code: 'CL', name: 'Chile' },
-      { code: 'CN', name: 'China' },
-      { code: 'CX', name: 'Christmas Island' },
-      { code: 'CP', name: 'Clipperton Island' },
-      { code: 'CC', name: 'Cocos [Keeling] Islands' },
-      { code: 'CO', name: 'Colombia' },
-      { code: 'KM', name: 'Comoros' },
-      { code: 'CG', name: 'Congo - Brazzaville' },
-      { code: 'CD', name: 'Congo - Kinshasa' },
-      { code: 'CD', name: 'Congo [DRC]' },
-      { code: 'CG', name: 'Congo [Republic]' },
-      { code: 'CK', name: 'Cook Islands' },
-      { code: 'CR', name: 'Costa Rica' },
-      { code: 'CI', name: 'Côte d’Ivoire' },
-      { code: 'HR', name: 'Croatia' },
-      { code: 'CU', name: 'Cuba' },
-      { code: 'CY', name: 'Cyprus' },
-      { code: 'CZ', name: 'Czech Republic' },
-      { code: 'DK', name: 'Denmark' },
-      { code: 'DG', name: 'Diego Garcia' },
-      { code: 'DJ', name: 'Djibouti' },
-      { code: 'DM', name: 'Dominica' },
-      { code: 'DO', name: 'Dominican Republic' },
-      { code: 'NQ', name: 'Dronning Maud Land' },
-      { code: 'TL', name: 'East Timor' },
-      { code: 'EC', name: 'Ecuador' },
-      { code: 'EG', name: 'Egypt' },
-      { code: 'SV', name: 'El Salvador' },
-      { code: 'GQ', name: 'Equatorial Guinea' },
-      { code: 'ER', name: 'Eritrea' },
-      { code: 'EE', name: 'Estonia' },
-      { code: 'ET', name: 'Ethiopia' },
-      { code: 'FK', name: 'Falkland Islands [Islas Malvinas]' },
-      { code: 'FK', name: 'Falkland Islands' },
-      { code: 'FO', name: 'Faroe Islands' },
-      { code: 'FJ', name: 'Fiji' },
-      { code: 'FI', name: 'Finland' },
-      { code: 'FR', name: 'France' },
-      { code: 'GF', name: 'French Guiana' },
-      { code: 'PF', name: 'French Polynesia' },
-      { code: 'FQ', name: 'French Southern and Antarctic Territories' },
-      { code: 'TF', name: 'French Southern Territories' },
-      { code: 'GA', name: 'Gabon' },
-      { code: 'GM', name: 'Gambia' },
-      { code: 'GE', name: 'Georgia' },
-      { code: 'DE', name: 'Germany' },
-      { code: 'GH', name: 'Ghana' },
-      { code: 'GI', name: 'Gibraltar' },
-      { code: 'GR', name: 'Greece' },
-      { code: 'GL', name: 'Greenland' },
-      { code: 'GD', name: 'Grenada' },
-      { code: 'GP', name: 'Guadeloupe' },
-      { code: 'GU', name: 'Guam' },
-      { code: 'GT', name: 'Guatemala' },
-      { code: 'GG', name: 'Guernsey' },
-      { code: 'GW', name: 'Guinea-Bissau' },
-      { code: 'GN', name: 'Guinea' },
-      { code: 'GY', name: 'Guyana' },
-      { code: 'HT', name: 'Haiti' },
-      { code: 'HM', name: 'Heard Island and McDonald Islands' },
-      { code: 'HN', name: 'Honduras' },
-      { code: 'HK', name: 'Hong Kong' },
-      { code: 'HU', name: 'Hungary' },
-      { code: 'IS', name: 'Iceland' },
-      { code: 'IN', name: 'India' },
-      { code: 'ID', name: 'Indonesia' },
-      { code: 'IR', name: 'Iran' },
-      { code: 'IQ', name: 'Iraq' },
-      { code: 'IE', name: 'Ireland' },
-      { code: 'IM', name: 'Isle of Man' },
-      { code: 'IL', name: 'Israel' },
-      { code: 'IT', name: 'Italy' },
-      { code: 'CI', name: 'Ivory Coast' },
-      { code: 'JM', name: 'Jamaica' },
-      { code: 'JP', name: 'Japan' },
-      { code: 'JE', name: 'Jersey' },
-      { code: 'JT', name: 'Johnston Island' },
-      { code: 'JO', name: 'Jordan' },
-      { code: 'KZ', name: 'Kazakhstan' },
-      { code: 'KE', name: 'Kenya' },
-      { code: 'KI', name: 'Kiribati' },
-      { code: 'KW', name: 'Kuwait' },
-      { code: 'KG', name: 'Kyrgyzstan' },
-      { code: 'LA', name: 'Laos' },
-      { code: 'LV', name: 'Latvia' },
-      { code: 'LB', name: 'Lebanon' },
-      { code: 'LS', name: 'Lesotho' },
-      { code: 'LR', name: 'Liberia' },
-      { code: 'LY', name: 'Libya' },
-      { code: 'LI', name: 'Liechtenstein' },
-      { code: 'LT', name: 'Lithuania' },
-      { code: 'LU', name: 'Luxembourg' },
-      { code: 'MO', name: 'Macau SAR China' },
-      { code: 'MO', name: 'Macau' },
-      { code: 'MK', name: 'Macedonia [FYROM]' },
-      { code: 'MK', name: 'Macedonia' },
-      { code: 'MG', name: 'Madagascar' },
-      { code: 'MW', name: 'Malawi' },
-      { code: 'MY', name: 'Malaysia' },
-      { code: 'MV', name: 'Maldives' },
-      { code: 'ML', name: 'Mali' },
-      { code: 'MT', name: 'Malta' },
-      { code: 'MH', name: 'Marshall Islands' },
-      { code: 'MQ', name: 'Martinique' },
-      { code: 'MR', name: 'Mauritania' },
-      { code: 'MU', name: 'Mauritius' },
-      { code: 'YT', name: 'Mayotte' },
-      { code: 'FX', name: 'Metropolitan France' },
-      { code: 'MX', name: 'Mexico' },
-      { code: 'FM', name: 'Micronesia' },
-      { code: 'MI', name: 'Midway Islands' },
-      { code: 'MD', name: 'Moldova' },
-      { code: 'MC', name: 'Monaco' },
-      { code: 'MN', name: 'Mongolia' },
-      { code: 'ME', name: 'Montenegro' },
-      { code: 'MS', name: 'Montserrat' },
-      { code: 'MA', name: 'Morocco' },
-      { code: 'MZ', name: 'Mozambique' },
-      { code: 'MM', name: 'Myanmar [Burma]' },
-      { code: 'NA', name: 'Namibia' },
-      { code: 'NR', name: 'Nauru' },
-      { code: 'NP', name: 'Nepal' },
-      { code: 'AN', name: 'Netherlands Antilles' },
-      { code: 'NL', name: 'Netherlands' },
-      { code: 'NC', name: 'New Caledonia' },
-      { code: 'NZ', name: 'New Zealand' },
-      { code: 'NI', name: 'Nicaragua' },
-      { code: 'NE', name: 'Niger' },
-      { code: 'NG', name: 'Nigeria' },
-      { code: 'NU', name: 'Niue' },
-      { code: 'NF', name: 'Norfolk Island' },
-      { code: 'KP', name: 'North Korea' },
-      { code: 'VD', name: 'North Vietnam' },
-      { code: 'MP', name: 'Northern Mariana Islands' },
-      { code: 'NO', name: 'Norway' },
-      { code: 'OM', name: 'Oman' },
-      { code: 'QO', name: 'Outlying Oceania' },
-      { code: 'PC', name: 'Pacific Islands Trust Territory' },
-      { code: 'PK', name: 'Pakistan' },
-      { code: 'PW', name: 'Palau' },
-      { code: 'PS', name: 'Palestinian Territories' },
-      { code: 'PZ', name: 'Panama Canal Zone' },
-      { code: 'PA', name: 'Panama' },
-      { code: 'PG', name: 'Papua New Guinea' },
-      { code: 'PY', name: 'Paraguay' },
-      { code: 'YD', name: "People's Democratic Republic of Yemen" },
-      { code: 'PE', name: 'Peru' },
-      { code: 'PH', name: 'Philippines' },
-      { code: 'PN', name: 'Pitcairn Islands' },
-      { code: 'PL', name: 'Poland' },
-      { code: 'PT', name: 'Portugal' },
-      { code: 'PR', name: 'Puerto Rico' },
-      { code: 'QA', name: 'Qatar' },
-      { code: 'RE', name: 'Réunion' },
-      { code: 'RO', name: 'Romania' },
-      { code: 'RU', name: 'Russia' },
-      { code: 'RW', name: 'Rwanda' },
-      { code: 'BL', name: 'Saint Barthélemy' },
-      { code: 'SH', name: 'Saint Helena' },
-      { code: 'KN', name: 'Saint Kitts and Nevis' },
-      { code: 'LC', name: 'Saint Lucia' },
-      { code: 'MF', name: 'Saint Martin' },
-      { code: 'PM', name: 'Saint Pierre and Miquelon' },
-      { code: 'VC', name: 'Saint Vincent and the Grenadines' },
-      { code: 'WS', name: 'Samoa' },
-      { code: 'SM', name: 'San Marino' },
-      { code: 'ST', name: 'São Tomé and Príncipe' },
-      { code: 'SA', name: 'Saudi Arabia' },
-      { code: 'SN', name: 'Senegal' },
-      { code: 'CS', name: 'Serbia and Montenegro' },
-      { code: 'RS', name: 'Serbia' },
-      { code: 'SC', name: 'Seychelles' },
-      { code: 'SL', name: 'Sierra Leone' },
-      { code: 'SG', name: 'Singapore' },
-      { code: 'SK', name: 'Slovakia' },
-      { code: 'SI', name: 'Slovenia' },
-      { code: 'SB', name: 'Solomon Islands' },
-      { code: 'SO', name: 'Somalia' },
-      { code: 'ZA', name: 'South Africa' },
-      { code: 'GS', name: 'South Georgia and the South Sandwich Islands' },
-      { code: 'KR', name: 'South Korea' },
-      { code: 'ES', name: 'Spain' },
-      { code: 'LK', name: 'Sri Lanka' },
-      { code: 'SD', name: 'Sudan' },
-      { code: 'SR', name: 'Suriname' },
-      { code: 'SJ', name: 'Svalbard and Jan Mayen' },
-      { code: 'SZ', name: 'Swaziland' },
-      { code: 'SE', name: 'Sweden' },
-      { code: 'CH', name: 'Switzerland' },
-      { code: 'SY', name: 'Syria' },
-      { code: 'TW', name: 'Taiwan' },
-      { code: 'TJ', name: 'Tajikistan' },
-      { code: 'TZ', name: 'Tanzania' },
-      { code: 'TH', name: 'Thailand' },
-      { code: 'TL', name: 'Timor-Leste' },
-      { code: 'TG', name: 'Togo' },
-      { code: 'TK', name: 'Tokelau' },
-      { code: 'TO', name: 'Tonga' },
-      { code: 'TT', name: 'Trinidad and Tobago' },
-      { code: 'TA', name: 'Tristan da Cunha' },
-      { code: 'TN', name: 'Tunisia' },
-      { code: 'TR', name: 'Turkey' },
-      { code: 'TM', name: 'Turkmenistan' },
-      { code: 'TC', name: 'Turks and Caicos Islands' },
-      { code: 'TV', name: 'Tuvalu' },
-      { code: 'UM', name: 'U.S. Minor Outlying Islands' },
-      { code: 'PU', name: 'U.S. Miscellaneous Pacific Islands' },
-      { code: 'VI', name: 'U.S. Virgin Islands' },
-      { code: 'UG', name: 'Uganda' },
-      { code: 'UA', name: 'Ukraine' },
-      { code: 'AE', name: 'United Arab Emirates' },
-      { code: 'GB', name: 'United Kingdom' },
-      { code: 'US', name: 'United States' },
-      { code: 'UY', name: 'Uruguay' },
-      { code: 'UZ', name: 'Uzbekistan' },
-      { code: 'VU', name: 'Vanuatu' },
-      { code: 'VA', name: 'Vatican City' },
-      { code: 'VE', name: 'Venezuela' },
-      { code: 'VN', name: 'Vietnam' },
-      { 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: 'ZW', name: 'Zimbabwe' },
-    ]
-  }
-)
-
-App.controller(
-  'StudentCheckModalController',
-  function ($scope, $modalInstance, eventTracking) {
-    $modalInstance.rendered.then(() => {
-      eventTracking.sendMB('student-check-displayed')
-    })
-
-    $scope.browsePlans = () => {
-      if (document.referrer?.includes('/user/subscription/choose-your-plan')) {
-        // redirect to interstitial page with `itm_referrer` param
-        assign(
-          '/user/subscription/choose-your-plan?itm_referrer=student-status-declined'
-        )
-      } else {
-        // redirect to plans page with `itm_referrer` param
-        assign('/user/subscription/plans?itm_referrer=student-status-declined')
-      }
-    }
-
-    $scope.confirm = () => $modalInstance.dismiss('cancel')
-  }
-)

+ 0 - 451
services/web/frontend/js/main/subscription-dashboard.js

@@ -1,451 +0,0 @@
-import _ from 'lodash'
-/* global recurly */
-
-/* eslint-disable
-    camelcase,
-    max-len,
-    no-return-assign,
-    no-unused-vars,
-*/
-// TODO: This file was created by bulk-decaffeinate.
-// Fix any style issues and re-enable lint.
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * DS104: Avoid inline assignments
- * DS204: Change includes calls to have a more natural evaluation order
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
-import App from '../base'
-import getMeta from '../utils/meta'
-const SUBSCRIPTION_URL = '/user/subscription/update'
-
-const GROUP_PLAN_MODAL_OPTIONS = getMeta('ol-groupPlanModalOptions')
-
-const ensureRecurlyIsSetup = _.once(() => {
-  if (typeof recurly === 'undefined' || !recurly) {
-    return false
-  }
-  recurly.configure(getMeta('ol-recurlyApiKey'))
-  return true
-})
-
-function getPricePerUser(price, currencySymbol, size) {
-  let perUserPrice = price / size
-  if (perUserPrice % 1 !== 0) {
-    perUserPrice = perUserPrice.toFixed(2)
-  }
-  return `${currencySymbol}${perUserPrice}`
-}
-
-App.controller('MetricsEmailController', function ($scope, $http) {
-  $scope.institutionEmailSubscription = function (institutionId) {
-    const inst = _.find(window.managedInstitutions, function (institution) {
-      return institution.v1Id === parseInt(institutionId)
-    })
-    if (inst.metricsEmail.optedOutUserIds.includes(window.user_id)) {
-      return 'Subscribe'
-    } else {
-      return 'Unsubscribe'
-    }
-  }
-
-  $scope.changeInstitutionalEmailSubscription = function (institutionId) {
-    $scope.subscriptionChanging = true
-    return $http({
-      method: 'POST',
-      url: `/institutions/${institutionId}/emailSubscription`,
-      headers: {
-        'X-CSRF-Token': window.csrfToken,
-      },
-    }).then(function successCallback(response) {
-      window.managedInstitutions = _.map(
-        window.managedInstitutions,
-        function (institution) {
-          if (institution.v1Id === parseInt(institutionId)) {
-            institution.metricsEmail.optedOutUserIds = response.data
-          }
-          return institution
-        }
-      )
-      $scope.subscriptionChanging = false
-    })
-  }
-})
-
-App.factory('RecurlyPricing', function ($q, MultiCurrencyPricing) {
-  return {
-    loadDisplayPriceWithTax: function (planCode, currency, taxRate) {
-      if (!ensureRecurlyIsSetup()) return
-      const currencySymbol = MultiCurrencyPricing.plans[currency].symbol
-      const pricing = recurly.Pricing()
-      return $q(function (resolve, reject) {
-        pricing
-          .plan(planCode, { quantity: 1 })
-          .currency(currency)
-          .done(function (price) {
-            const totalPriceExTax = parseFloat(price.next.total)
-            let taxAmount = totalPriceExTax * taxRate
-            if (isNaN(taxAmount)) {
-              taxAmount = 0
-            }
-            let total = totalPriceExTax + taxAmount
-            if (total % 1 !== 0) {
-              total = total.toFixed(2)
-            }
-            resolve({
-              total: `${currencySymbol}${total}`,
-              totalValue: total,
-              subtotal: `${currencySymbol}${totalPriceExTax.toFixed(2)}`,
-              tax: `${currencySymbol}${taxAmount.toFixed(2)}`,
-              includesTax: taxAmount !== 0,
-            })
-          })
-      })
-    },
-  }
-})
-
-App.controller('ChangePlanToGroupFormController', function ($scope, $modal) {
-  if (!ensureRecurlyIsSetup()) return
-
-  const subscription = getMeta('ol-subscription')
-  const currency = subscription.recurly.currency
-
-  const validCurrencies = GROUP_PLAN_MODAL_OPTIONS.currencies.map(
-    item => item.code
-  )
-
-  if (validCurrencies.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, RecurlyPricing) {
-    $scope.options = GROUP_PLAN_MODAL_OPTIONS
-
-    $scope.groupPlans = 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 subscription = getMeta('ol-subscription')
-      const { taxRate } = subscription.recurly
-      const { usage, plan_code, currency, size } = $scope.selected
-      $scope.discountEligible = size >= 10
-      const recurlyPricePlaceholder = { total: '...' }
-      let perUserDisplayPricePlaceholder = '...'
-      const currencySymbol = $scope.options.currencySymbols[currency]
-      if (taxRate === 0) {
-        const basePriceInCents =
-          $scope.groupPlans[usage][plan_code][currency][size].price_in_cents
-        const basePriceInUnit = (basePriceInCents / 100).toFixed()
-        recurlyPricePlaceholder.total = `${currencySymbol}${basePriceInUnit}`
-        perUserDisplayPricePlaceholder = getPricePerUser(
-          basePriceInUnit,
-          currencySymbol,
-          size
-        )
-      }
-      $scope.recurlyPrice = recurlyPricePlaceholder // Placeholder while we talk to recurly
-      $scope.perUserDisplayPrice = perUserDisplayPricePlaceholder // Placeholder while we talk to recurly
-      const recurlyPlanCode = `group_${plan_code}_${size}_${usage}`
-      RecurlyPricing.loadDisplayPriceWithTax(
-        recurlyPlanCode,
-        currency,
-        taxRate
-      ).then(price => {
-        $scope.recurlyPrice = price
-        $scope.perUserDisplayPrice = getPricePerUser(
-          price.totalValue,
-          currencySymbol,
-          size
-        )
-      })
-    }
-
-    $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) {
-    if (!ensureRecurlyIsSetup()) return
-
-    function stripCentsIfZero(displayPrice) {
-      return displayPrice ? displayPrice.replace(/\.00$/, '') : '...'
-    }
-
-    $scope.changePlan = () =>
-      $modal.open({
-        templateUrl: 'confirmChangePlanModalTemplate',
-        controller: 'ConfirmChangePlanController',
-        scope: $scope,
-      })
-
-    $scope.cancelPendingPlanChange = () =>
-      $modal.open({
-        templateUrl: 'cancelPendingPlanChangeModalTemplate',
-        controller: 'CancelPendingPlanChangeController',
-        scope: $scope,
-      })
-
-    $scope.$watch('plan', function (plan) {
-      if (!plan) return
-      const planCodesChangingAtTermEnd = getMeta(
-        'ol-planCodesChangingAtTermEnd'
-      )
-      $scope.planChangesAtTermEnd = false
-      if (
-        planCodesChangingAtTermEnd &&
-        planCodesChangingAtTermEnd.indexOf(plan.planCode) > -1
-      ) {
-        $scope.planChangesAtTermEnd = true
-      }
-      const planCode = plan.planCode
-      const subscription = getMeta('ol-subscription')
-      const { currency, taxRate } = subscription.recurly
-      if (subscription.recurly.displayPrice) {
-        if (subscription.pendingPlan?.planCode === planCode) {
-          $scope.displayPrice = stripCentsIfZero(
-            subscription.recurly.displayPrice
-          )
-          return
-        }
-        if (subscription.planCode === planCode) {
-          if (subscription.pendingPlan) {
-            $scope.displayPrice = stripCentsIfZero(
-              subscription.recurly.currentPlanDisplayPrice
-            )
-          } else {
-            $scope.displayPrice = stripCentsIfZero(
-              subscription.recurly.displayPrice
-            )
-          }
-          return
-        }
-      }
-      $scope.displayPrice = '...' // Placeholder while we talk to recurly
-      RecurlyPricing.loadDisplayPriceWithTax(planCode, currency, taxRate).then(
-        recurlyPrice => {
-          $scope.displayPrice = recurlyPrice.total
-        }
-      )
-    })
-  }
-)
-
-App.controller(
-  'ConfirmChangePlanController',
-  function ($scope, $modalInstance, $http) {
-    $scope.confirmChangePlan = function () {
-      const body = {
-        plan_code: $scope.plan.planCode,
-        _csrf: window.csrfToken,
-      }
-
-      $scope.genericError = false
-      $scope.inflight = true
-
-      return $http
-        .post(`${SUBSCRIPTION_URL}?origin=confirmChangePlan`, body)
-        .then(() => location.reload())
-        .catch(() => {
-          $scope.genericError = true
-          $scope.inflight = false
-        })
-    }
-
-    return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
-  }
-)
-
-App.controller(
-  'CancelPendingPlanChangeController',
-  function ($scope, $modalInstance, $http) {
-    $scope.confirmCancelPendingPlanChange = function () {
-      const body = {
-        _csrf: window.csrfToken,
-      }
-
-      $scope.genericError = false
-      $scope.inflight = true
-
-      return $http
-        .post('/user/subscription/cancel-pending', body)
-        .then(() => location.reload())
-        .catch(() => {
-          $scope.genericError = true
-          $scope.inflight = false
-        })
-    }
-
-    return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
-  }
-)
-
-App.controller(
-  'LeaveGroupModalController',
-  function ($scope, $modalInstance, $http) {
-    $scope.confirmLeaveGroup = function () {
-      $scope.inflight = true
-      return $http({
-        url: '/subscription/group/user',
-        method: 'DELETE',
-        params: {
-          subscriptionId: $scope.subscriptionId,
-          _csrf: window.csrfToken,
-        },
-      })
-        .then(() => window.location.reload())
-        .catch(() => console.log('something went wrong changing plan'))
-    }
-
-    return ($scope.cancel = () => $modalInstance.dismiss('cancel'))
-  }
-)
-
-App.controller('GroupMembershipController', function ($scope, $modal) {
-  $scope.removeSelfFromGroup = function (subscriptionId) {
-    $scope.subscriptionId = subscriptionId
-    return $modal.open({
-      templateUrl: 'LeaveGroupModalTemplate',
-      controller: 'LeaveGroupModalController',
-      scope: $scope,
-    })
-  }
-})
-
-App.controller('RecurlySubscriptionController', function ($scope) {
-  const recurlyIsSetup = ensureRecurlyIsSetup()
-  const subscription = getMeta('ol-subscription')
-  $scope.showChangePlanButton = recurlyIsSetup && !subscription.groupPlan
-  if (
-    window.subscription.recurly.account.has_past_due_invoice &&
-    window.subscription.recurly.account.has_past_due_invoice._ === 'true'
-  ) {
-    $scope.showChangePlanButton = false
-  }
-  $scope.recurlyLoadError = !recurlyIsSetup
-
-  $scope.switchToDefaultView = () => {
-    $scope.showCancellation = false
-    $scope.showChangePlan = false
-  }
-  $scope.switchToDefaultView()
-
-  $scope.switchToCancellationView = () => {
-    $scope.showCancellation = true
-    $scope.showChangePlan = false
-  }
-
-  $scope.switchToChangePlanView = () => {
-    $scope.showCancellation = false
-    $scope.showChangePlan = true
-  }
-})
-
-App.controller(
-  'RecurlyCancellationController',
-  function ($scope, RecurlyPricing, $http) {
-    if (!ensureRecurlyIsSetup()) return
-    const subscription = getMeta('ol-subscription')
-    const sevenDaysTime = new Date()
-    sevenDaysTime.setDate(sevenDaysTime.getDate() + 7)
-    const freeTrialEndDate = new Date(subscription.recurly.trial_ends_at)
-    const freeTrialInFuture = freeTrialEndDate > new Date()
-    const freeTrialExpiresUnderSevenDays = freeTrialEndDate < sevenDaysTime
-
-    const isMonthlyCollab =
-      subscription.plan.planCode.indexOf('collaborator') !== -1 &&
-      subscription.plan.planCode.indexOf('ann') === -1 &&
-      !subscription.groupPlan
-    const stillInFreeTrial = freeTrialInFuture && freeTrialExpiresUnderSevenDays
-
-    if (isMonthlyCollab && stillInFreeTrial) {
-      $scope.showExtendFreeTrial = true
-    } else if (isMonthlyCollab && !stillInFreeTrial) {
-      $scope.showDowngrade = true
-    } else {
-      $scope.showBasicCancel = true
-    }
-
-    const planCode = 'paid-personal'
-    const { currency, taxRate } = subscription.recurly
-    $scope.personalDisplayPrice = '...' // Placeholder while we talk to recurly
-    RecurlyPricing.loadDisplayPriceWithTax(planCode, currency, taxRate).then(
-      price => {
-        $scope.personalDisplayPrice = price.total
-      }
-    )
-
-    $scope.downgradeToPaidPersonal = function () {
-      const body = {
-        plan_code: planCode,
-        _csrf: window.csrfToken,
-      }
-      $scope.inflight = true
-      return $http
-        .post(`${SUBSCRIPTION_URL}?origin=downgradeToPaidPersonal`, body)
-        .then(() => location.reload())
-        .catch(() => console.log('something went wrong changing plan'))
-    }
-
-    $scope.cancelSubscription = function () {
-      const body = { _csrf: window.csrfToken }
-
-      $scope.inflight = true
-      return $http
-        .post('/user/subscription/cancel', body)
-        .then(() => (location.href = '/user/subscription/canceled'))
-        .catch(() => console.log('something went wrong changing plan'))
-    }
-
-    $scope.extendTrial = function () {
-      const body = { _csrf: window.csrfToken }
-      $scope.inflight = true
-      return $http
-        .put('/user/subscription/extend', body)
-        .then(() => location.reload())
-        .catch(() => console.log('something went wrong changing plan'))
-    }
-  }
-)

+ 0 - 13
services/web/frontend/js/main/subscription/upgrade-subscription.js

@@ -1,13 +0,0 @@
-import App from '../../base'
-
-export default App.controller(
-  'UpgradeSubscriptionController',
-  function ($scope, eventTracking) {
-    $scope.upgradeSubscription = function () {
-      eventTracking.send('subscription-funnel', 'subscription-page', 'upgrade')
-      eventTracking.sendMB('upgrade-button-click', {
-        source: 'subscription-page',
-      })
-    }
-  }
-)

+ 0 - 126
services/web/frontend/js/main/user-membership.js

@@ -1,126 +0,0 @@
-import _ from 'lodash'
-/* eslint-disable
-    max-len,
-    no-return-assign,
-*/
-// TODO: This file was created by bulk-decaffeinate.
-// Fix any style issues and re-enable lint.
-/*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
-import App from '../base'
-App.controller('UserMembershipController', function ($scope, queuedHttp) {
-  $scope.users = window.users
-  $scope.groupSize = window.groupSize
-  $scope.paths = window.paths
-  $scope.selectedUsers = []
-
-  $scope.inputs = {
-    addMembers: {
-      content: '',
-      error: false,
-      errorMessage: null,
-      inflightCount: 0,
-    },
-    removeMembers: {
-      error: false,
-      errorMessage: null,
-    },
-  }
-
-  const parseEmails = function (emailsString) {
-    const regexBySpaceOrComma = /[\s,]+/
-    let emails = emailsString.split(regexBySpaceOrComma)
-    emails = _.map(emails, email => (email = email.trim()))
-    emails = _.filter(emails, email => email.indexOf('@') !== -1)
-    return emails
-  }
-
-  $scope.addMembers = function () {
-    $scope.inputs.addMembers.error = false
-    $scope.inputs.addMembers.errorMessage = null
-    $scope.inputs.addMembers.inflightCount = 0
-    const emails = parseEmails($scope.inputs.addMembers.content)
-    return Array.from(emails).map(email => {
-      $scope.inputs.addMembers.inflightCount += 1
-      return queuedHttp
-        .post(window.paths.addMember, {
-          email,
-          _csrf: window.csrfToken,
-        })
-        .then(function (response) {
-          $scope.inputs.addMembers.inflightCount -= 1
-          const { data } = response
-          if (data.user != null) {
-            const alreadyListed = $scope.users.find(
-              scopeUser => scopeUser.email === data.user.email
-            )
-            if (!alreadyListed) {
-              $scope.users.push(data.user)
-            }
-          }
-          return ($scope.inputs.addMembers.content = '')
-        })
-        .catch(function (response) {
-          $scope.inputs.addMembers.inflightCount -= 1
-          const { data } = response
-          $scope.inputs.addMembers.error = true
-          return ($scope.inputs.addMembers.errorMessage =
-            data.error != null ? data.error.message : undefined)
-        })
-    })
-  }
-
-  $scope.removeMembers = function () {
-    $scope.inputs.removeMembers.error = false
-    $scope.inputs.removeMembers.errorMessage = null
-    for (const user of Array.from($scope.selectedUsers)) {
-      ;(function (user) {
-        let url
-        if (window.paths.removeInvite && user.invite && user._id == null) {
-          url = `${window.paths.removeInvite}/${encodeURIComponent(user.email)}`
-        } else if (window.paths.removeMember && user._id != null) {
-          url = `${window.paths.removeMember}/${user._id}`
-        } else {
-          return
-        }
-        return queuedHttp({
-          method: 'DELETE',
-          url,
-          headers: {
-            'X-Csrf-Token': window.csrfToken,
-          },
-        })
-          .then(function () {
-            const index = $scope.users.indexOf(user)
-            if (index === -1) {
-              return
-            }
-            return $scope.users.splice(index, 1)
-          })
-          .catch(function (response) {
-            const { data } = response
-            $scope.inputs.removeMembers.error = true
-            return ($scope.inputs.removeMembers.errorMessage =
-              data.error != null ? data.error.message : undefined)
-          })
-      })(user)
-    }
-    return $scope.updateSelectedUsers
-  }
-
-  return ($scope.updateSelectedUsers = () =>
-    ($scope.selectedUsers = $scope.users.filter(user => user.selected)))
-})
-
-export default App.controller('UserMembershipListItemController', $scope =>
-  $scope.$watch('user.selected', function (value) {
-    if (value != null) {
-      return $scope.updateSelectedUsers()
-    }
-  })
-)

+ 0 - 2
services/web/locales/en.json

@@ -272,7 +272,6 @@
   "contact_message_label": "Message",
   "contact_message_label": "Message",
   "contact_sales": "Contact Sales",
   "contact_sales": "Contact Sales",
   "contact_support_to_change_group_subscription": "Please <0>contact support</0> if you wish to change your group subscription.",
   "contact_support_to_change_group_subscription": "Please <0>contact support</0> if you wish to change your group subscription.",
-  "contact_support_to_upgrade_to_group_subscription": "Please <0>contact support</0> if you wish to be upgraded to a group subscription.",
   "contact_us": "Contact Us",
   "contact_us": "Contact Us",
   "contact_us_lowercase": "Contact us",
   "contact_us_lowercase": "Contact us",
   "continue": "Continue",
   "continue": "Continue",
@@ -1476,7 +1475,6 @@
   "stop_on_first_error_enabled_description": "<0>“Stop on first error” is enabled.</0> Disabling it may allow the compiler to produce a PDF (but your project will still have errors).",
   "stop_on_first_error_enabled_description": "<0>“Stop on first error” is enabled.</0> Disabling it may allow the compiler to produce a PDF (but your project will still have errors).",
   "stop_on_first_error_enabled_title": "No PDF: Stop on first error enabled",
   "stop_on_first_error_enabled_title": "No PDF: Stop on first error enabled",
   "stop_on_validation_error": "Check syntax before compile",
   "stop_on_validation_error": "Check syntax before compile",
-  "stop_your_subscription": "Stop Your Subscription",
   "store_your_work": "Store your work on your own infrastructure",
   "store_your_work": "Store your work on your own infrastructure",
   "student": "Student",
   "student": "Student",
   "student_and_faculty_support_make_difference": "Student and faculty support make a difference! We can share this information with our contacts at your university when discussing an Overleaf institutional account.",
   "student_and_faculty_support_make_difference": "Student and faculty support make a difference! We can share this information with our contacts at your university when discussing an Overleaf institutional account.",

+ 14 - 5
services/web/test/unit/src/Subscription/SubscriptionControllerTests.js

@@ -78,6 +78,9 @@ describe('SubscriptionController', function () {
       promises: {
       promises: {
         buildUsersSubscriptionViewModel: sinon.stub().resolves({}),
         buildUsersSubscriptionViewModel: sinon.stub().resolves({}),
       },
       },
+      buildPlansListForSubscriptionDash: sinon
+        .stub()
+        .returns({ plans: [], planCodesChangingAtTermEnd: [] }),
     }
     }
     this.settings = {
     this.settings = {
       coupon_codes: {
       coupon_codes: {
@@ -283,10 +286,10 @@ describe('SubscriptionController', function () {
       describe('with a valid plan code', function () {
       describe('with a valid plan code', function () {
         it('should render the new subscription page', function (done) {
         it('should render the new subscription page', function (done) {
           this.res.render = (page, opts) => {
           this.res.render = (page, opts) => {
-            page.should.equal('subscriptions/new-refreshed')
+            page.should.equal('subscriptions/new-react')
             done()
             done()
           }
           }
-          this.SubscriptionController.paymentPage(this.req, this.res)
+          this.SubscriptionController.paymentPage(this.req, this.res, done)
         })
         })
       })
       })
     })
     })
@@ -407,7 +410,7 @@ describe('SubscriptionController', function () {
         }
         }
       )
       )
       this.res.render = (url, variables) => {
       this.res.render = (url, variables) => {
-        url.should.equal('subscriptions/successful-subscription')
+        url.should.equal('subscriptions/successful-subscription-react')
         assert.deepEqual(variables, {
         assert.deepEqual(variables, {
           title: 'thank_you',
           title: 'thank_you',
           personalSubscription: 'foo',
           personalSubscription: 'foo',
@@ -448,13 +451,19 @@ describe('SubscriptionController', function () {
       this.SubscriptionViewModelBuilder.buildPlansList.returns(
       this.SubscriptionViewModelBuilder.buildPlansList.returns(
         (this.plans = { plans: 'mock' })
         (this.plans = { plans: 'mock' })
       )
       )
+      this.SubscriptionViewModelBuilder.buildPlansListForSubscriptionDash.returns(
+        {
+          plans: this.plans,
+          planCodesChangingAtTermEnd: [],
+        }
+      )
       this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(false)
       this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(false)
       this.res.render = (view, data) => {
       this.res.render = (view, data) => {
         this.data = data
         this.data = data
-        expect(view).to.equal('subscriptions/dashboard')
+        expect(view).to.equal('subscriptions/dashboard-react')
         done()
         done()
       }
       }
-      this.SubscriptionController.userSubscriptionPage(this.req, this.res)
+      this.SubscriptionController.userSubscriptionPage(this.req, this.res, done)
     })
     })
 
 
     it('should load the personal, groups and v1 subscriptions', function () {
     it('should load the personal, groups and v1 subscriptions', function () {

+ 5 - 16
services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js

@@ -110,13 +110,9 @@ describe('UserMembershipController', function () {
     it('render group view', async function () {
     it('render group view', async function () {
       return await this.UserMembershipController.manageGroupMembers(this.req, {
       return await this.UserMembershipController.manageGroupMembers(this.req, {
         render: (viewPath, viewParams) => {
         render: (viewPath, viewParams) => {
-          expect(viewPath).to.equal('user_membership/index')
+          expect(viewPath).to.equal('user_membership/group-members-react')
           expect(viewParams.users).to.deep.equal(this.users)
           expect(viewParams.users).to.deep.equal(this.users)
           expect(viewParams.groupSize).to.equal(this.subscription.membersLimit)
           expect(viewParams.groupSize).to.equal(this.subscription.membersLimit)
-          expect(viewParams.translations.title).to.equal('group_subscription')
-          expect(viewParams.paths.addMember).to.equal(
-            `/manage/groups/${this.subscription._id}/invites`
-          )
         },
         },
       })
       })
     })
     })
@@ -125,13 +121,8 @@ describe('UserMembershipController', function () {
       this.req.entityConfig = EntityConfigs.groupManagers
       this.req.entityConfig = EntityConfigs.groupManagers
       return await this.UserMembershipController.manageGroupManagers(this.req, {
       return await this.UserMembershipController.manageGroupManagers(this.req, {
         render: (viewPath, viewParams) => {
         render: (viewPath, viewParams) => {
-          expect(viewPath).to.equal('user_membership/index')
+          expect(viewPath).to.equal('user_membership/group-managers-react')
           expect(viewParams.groupSize).to.equal(undefined)
           expect(viewParams.groupSize).to.equal(undefined)
-          expect(viewParams.translations.title).to.equal('group_subscription')
-          expect(viewParams.translations.subtitle).to.equal(
-            'managers_management'
-          )
-          expect(viewParams.paths.exportMembers).to.be.undefined
         },
         },
       })
       })
     })
     })
@@ -143,13 +134,11 @@ describe('UserMembershipController', function () {
         this.req,
         this.req,
         {
         {
           render: (viewPath, viewParams) => {
           render: (viewPath, viewParams) => {
-            expect(viewPath).to.equal('user_membership/index')
+            expect(viewPath).to.equal(
+              'user_membership/institution-managers-react'
+            )
             expect(viewParams.name).to.equal('Test Institution Name')
             expect(viewParams.name).to.equal('Test Institution Name')
             expect(viewParams.groupSize).to.equal(undefined)
             expect(viewParams.groupSize).to.equal(undefined)
-            expect(viewParams.translations.title).to.equal(
-              'institution_account'
-            )
-            expect(viewParams.paths.exportMembers).to.be.undefined
           },
           },
         }
         }
       )
       )