Răsfoiți Sursa

Merge pull request #24430 from overleaf/ii-flexible-group-licensing-tear-down-feature-flag

[web] Tear down flexible group licensing feature flag

GitOrigin-RevId: 181713f1f2371b51cbc4256debea59bbcf3668f3
ilkin-overleaf 1 an în urmă
părinte
comite
61ce012fb5
19 a modificat fișierele cu 94 adăugiri și 484 ștergeri
  1. 0 8
      services/web/app/src/Features/Subscription/SubscriptionController.js
  2. 0 17
      services/web/app/src/Features/Subscription/SubscriptionGroupController.mjs
  3. 0 5
      services/web/app/src/Features/Subscription/SubscriptionRouter.mjs
  4. 0 6
      services/web/app/src/Features/UserMembership/UserMembershipController.mjs
  5. 50 62
      services/web/app/views/subscriptions/plans/_plans_faq_tabs.pug
  6. 0 9
      services/web/frontend/extracted-translations.json
  7. 5 19
      services/web/frontend/js/features/group-management/components/group-members.tsx
  8. 1 5
      services/web/frontend/js/features/subscription/components/dashboard/group-settings-button.tsx
  9. 3 17
      services/web/frontend/js/features/subscription/components/dashboard/managed-group-subscriptions.tsx
  10. 1 17
      services/web/frontend/js/features/subscription/components/dashboard/personal-subscription.tsx
  11. 7 20
      services/web/frontend/js/features/subscription/components/dashboard/premium-features-link.tsx
  12. 0 221
      services/web/frontend/js/features/subscription/components/dashboard/states/active/active-ai-addon.tsx
  13. 0 9
      services/web/locales/en.json
  14. 15 29
      services/web/test/frontend/features/group-management/components/group-members.spec.tsx
  15. 3 3
      services/web/test/frontend/features/subscription/components/dashboard/managed-group-subscriptions.test.tsx
  16. 1 1
      services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx
  17. 2 5
      services/web/test/frontend/features/subscription/components/dashboard/subscription-dashboard.test.tsx
  18. 6 4
      services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx
  19. 0 27
      services/web/test/unit/src/Subscription/SubscriptionGroupControllerTests.mjs

+ 0 - 8
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -56,13 +56,6 @@ async function userSubscriptionPage(req, res) {
 
   const showGroupDiscount = groupPricingDiscount.variant === 'enabled'
 
-  const { variant: flexibleLicensingVariant } =
-    await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'flexible-group-licensing'
-    )
-
   const results =
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
       user,
@@ -147,7 +140,6 @@ async function userSubscriptionPage(req, res) {
           (managedUsersResults?.[0] === true ||
             groupSSOResults?.[0] === true) &&
           isGroupAdmin &&
-          flexibleLicensingVariant === 'enabled' &&
           plan?.canUseFlexibleLicensing
         )
       }

+ 0 - 17
services/web/app/src/Features/Subscription/SubscriptionGroupController.mjs

@@ -8,8 +8,6 @@ import SessionManager from '../Authentication/SessionManager.js'
 import UserAuditLogHandler from '../User/UserAuditLogHandler.js'
 import { expressify } from '@overleaf/promise-utils'
 import Modules from '../../infrastructure/Modules.js'
-import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
-import ErrorController from '../Errors/ErrorController.js'
 import UserGetter from '../User/UserGetter.js'
 import { Subscription } from '../../models/Subscription.js'
 import { isProfessionalGroupPlan } from './PlansHelper.mjs'
@@ -288,20 +286,6 @@ async function submitForm(req, res) {
   res.sendStatus(204)
 }
 
-async function flexibleLicensingSplitTest(req, res, next) {
-  const { variant } = await SplitTestHandler.promises.getAssignment(
-    req,
-    res,
-    'flexible-group-licensing'
-  )
-
-  if (variant !== 'enabled') {
-    return ErrorController.notFound(req, res)
-  }
-
-  next()
-}
-
 async function subscriptionUpgradePage(req, res) {
   try {
     const userId = SessionManager.getLoggedInUserId(req.session)
@@ -409,7 +393,6 @@ export default {
   removeSelfFromGroup: expressify(removeSelfFromGroup),
   addSeatsToGroupSubscription: expressify(addSeatsToGroupSubscription),
   submitForm: expressify(submitForm),
-  flexibleLicensingSplitTest: expressify(flexibleLicensingSplitTest),
   previewAddSeatsSubscriptionChange: expressify(
     previewAddSeatsSubscriptionChange
   ),

+ 0 - 5
services/web/app/src/Features/Subscription/SubscriptionRouter.mjs

@@ -73,7 +73,6 @@ export default {
       '/user/subscription/group/add-users',
       AuthenticationController.requireLogin(),
       RateLimiterMiddleware.rateLimit(subscriptionRateLimiter),
-      SubscriptionGroupController.flexibleLicensingSplitTest,
       SubscriptionGroupController.addSeatsToGroupSubscription
     )
 
@@ -108,7 +107,6 @@ export default {
       '/user/subscription/group/upgrade-subscription',
       AuthenticationController.requireLogin(),
       RateLimiterMiddleware.rateLimit(subscriptionRateLimiter),
-      SubscriptionGroupController.flexibleLicensingSplitTest,
       SubscriptionGroupController.subscriptionUpgradePage
     )
 
@@ -123,7 +121,6 @@ export default {
       '/user/subscription/group/missing-billing-information',
       AuthenticationController.requireLogin(),
       RateLimiterMiddleware.rateLimit(subscriptionRateLimiter),
-      SubscriptionGroupController.flexibleLicensingSplitTest,
       SubscriptionGroupController.missingBillingInformation
     )
 
@@ -131,7 +128,6 @@ export default {
       '/user/subscription/group/manually-collected-subscription',
       AuthenticationController.requireLogin(),
       RateLimiterMiddleware.rateLimit(subscriptionRateLimiter),
-      SubscriptionGroupController.flexibleLicensingSplitTest,
       SubscriptionGroupController.manuallyCollectedSubscription
     )
 
@@ -139,7 +135,6 @@ export default {
       '/user/subscription/group/subtotal-limit-exceeded',
       AuthenticationController.requireLogin(),
       RateLimiterMiddleware.rateLimit(subscriptionRateLimiter),
-      SubscriptionGroupController.flexibleLicensingSplitTest,
       SubscriptionGroupController.subtotalLimitExceeded
     )
 

+ 0 - 6
services/web/app/src/Features/UserMembership/UserMembershipController.mjs

@@ -32,12 +32,6 @@ async function manageGroupMembers(req, res, next) {
   )
   const ssoConfig = await SSOConfig.findById(subscription.ssoConfig).exec()
 
-  await SplitTestHandler.promises.getAssignment(
-    req,
-    res,
-    'flexible-group-licensing'
-  )
-
   await SplitTestHandler.promises.getAssignment(req, res, 'bootstrap-5-groups')
 
   const plan = PlansLocator.findLocalPlanInSettings(subscription.planCode)

+ 50 - 62
services/web/app/views/subscriptions/plans/_plans_faq_tabs.pug

@@ -165,56 +165,15 @@ mixin overleafGroupPlans()
 			.collapse(id="overleafGroupPlansQ3")
 				.custom-accordion-body
 					| The educational discount for group subscriptions is for students or faculty who are using Overleaf primarily for teaching. 
-		if canUseFlexibleLicensing
-			.custom-accordion-item
-				button.custom-accordion-header.collapsed(type="button" data-toggle="collapse" data-target="#overleafGroupPlansQ4" aria-expanded="false" aria-controls="overleafGroupPlansQ4")
-					| How do I add more licenses to my group subscription, and what will it cost?
-					span.custom-accordion-icon
-						i.material-symbols.material-symbols-outlined(aria-hidden="true") keyboard_arrow_down
-				.collapse(id="overleafGroupPlansQ4")
-					.custom-accordion-body
-						div 
-							| You can add up to 20 licenses using the 
-							a.inline-green-link(
-								target="_blank"
-								href="/user/subscription"
-								event-tracking="plans-page-click"
-								event-tracking-mb="true"
-								event-tracking-trigger="click"
-								event-segmentation={ button: 'contact', location: 'faq' }
-							)
-								span subscription management page
-							|  accessed by going to Account > 
-							a.inline-green-link(
-								target="_blank"
-								href="/user/subscription"
-								event-tracking="plans-page-click"
-								event-tracking-mb="true"
-								event-tracking-trigger="click"
-								event-segmentation={ button: 'contact', location: 'faq' }
-							)
-								span Subscription
-							|  when logged into Overleaf. The cost per license will be prorated at the current per license rate, and will end with your existing renewal date.
-						div.mt-2 
-							| If you need more than 20 licenses added to your subscription, please 
-							a.inline-green-link(
-								target="_blank"
-								href="/for/contact-sales"
-								event-tracking="plans-page-click"
-								event-tracking-mb="true"
-								event-tracking-trigger="click"
-								event-segmentation={ button: 'contact', location: 'faq' }
-							)
-								span contact the Sales team
-							| .
-			.custom-accordion-item
-				button.custom-accordion-header.collapsed(type="button" data-toggle="collapse" data-target="#overleafGroupPlansQ5" aria-expanded="false" aria-controls="overleafGroupPlansQ5")
-					| How do I upgrade my plan from Group Standard to Group Professional?
-					span.custom-accordion-icon
-						i.material-symbols.material-symbols-outlined(aria-hidden="true") keyboard_arrow_down
-				.collapse(id="overleafGroupPlansQ5")
-					.custom-accordion-body
-						| You can upgrade your plan from Group Standard to Group Professional on the 
+		.custom-accordion-item
+			button.custom-accordion-header.collapsed(type="button" data-toggle="collapse" data-target="#overleafGroupPlansQ4" aria-expanded="false" aria-controls="overleafGroupPlansQ4")
+				| How do I add more licenses to my group subscription, and what will it cost?
+				span.custom-accordion-icon
+					i.material-symbols.material-symbols-outlined(aria-hidden="true") keyboard_arrow_down
+			.collapse(id="overleafGroupPlansQ4")
+				.custom-accordion-body
+					div 
+						| You can add up to 20 licenses using the 
 						a.inline-green-link(
 							target="_blank"
 							href="/user/subscription"
@@ -224,15 +183,44 @@ mixin overleafGroupPlans()
 							event-segmentation={ button: 'contact', location: 'faq' }
 						)
 							span subscription management page
-						| .	
-		else
-			.custom-accordion-item
-				button.custom-accordion-header.collapsed(type="button" data-toggle="collapse" data-target="#overleafGroupPlansQ4" aria-expanded="false" aria-controls="overleafGroupPlansQ4")
-					| Can I add more users to my group subscription at a later date?
-					span.custom-accordion-icon
-						i.material-symbols.material-symbols-outlined(aria-hidden="true") keyboard_arrow_down
-				.collapse(id="overleafGroupPlansQ4")
-					.custom-accordion-body
-						| Yes. To add more users to your subscription you’ll need to 
-						button.btn-link.inline-green-link(data-ol-open-contact-form-modal="general")
-							span #{translate("contact_us")}
+						|  accessed by going to Account > 
+						a.inline-green-link(
+							target="_blank"
+							href="/user/subscription"
+							event-tracking="plans-page-click"
+							event-tracking-mb="true"
+							event-tracking-trigger="click"
+							event-segmentation={ button: 'contact', location: 'faq' }
+						)
+							span Subscription
+						|  when logged into Overleaf. The cost per license will be prorated at the current per license rate, and will end with your existing renewal date.
+					div.mt-2 
+						| If you need more than 20 licenses added to your subscription, please 
+						a.inline-green-link(
+							target="_blank"
+							href="/for/contact-sales"
+							event-tracking="plans-page-click"
+							event-tracking-mb="true"
+							event-tracking-trigger="click"
+							event-segmentation={ button: 'contact', location: 'faq' }
+						)
+							span contact the Sales team
+						| .
+		.custom-accordion-item
+			button.custom-accordion-header.collapsed(type="button" data-toggle="collapse" data-target="#overleafGroupPlansQ5" aria-expanded="false" aria-controls="overleafGroupPlansQ5")
+				| How do I upgrade my plan from Group Standard to Group Professional?
+				span.custom-accordion-icon
+					i.material-symbols.material-symbols-outlined(aria-hidden="true") keyboard_arrow_down
+			.collapse(id="overleafGroupPlansQ5")
+				.custom-accordion-body
+					| You can upgrade your plan from Group Standard to Group Professional on the 
+					a.inline-green-link(
+						target="_blank"
+						href="/user/subscription"
+						event-tracking="plans-page-click"
+						event-tracking-mb="true"
+						event-tracking-trigger="click"
+						event-segmentation={ button: 'contact', location: 'faq' }
+					)
+						span subscription management page
+					| .

+ 0 - 9
services/web/frontend/extracted-translations.json

@@ -74,13 +74,11 @@
   "add_files": "",
   "add_more_editors": "",
   "add_more_managers": "",
-  "add_more_members": "",
   "add_more_users": "",
   "add_more_users_to_my_plan": "",
   "add_new_email": "",
   "add_on": "",
   "add_ons": "",
-  "add_ons_are": "",
   "add_or_remove_project_from_tag": "",
   "add_overleaf_assist": "",
   "add_overleaf_assist_to_your_group_subscription": "",
@@ -609,7 +607,6 @@
   "get_error_assist": "",
   "get_exclusive_access_to_labs": "",
   "get_in_touch": "",
-  "get_most_subscription_by_checking_features": "",
   "get_most_subscription_discover_premium_features": "",
   "git": "",
   "git_authentication_token": "",
@@ -952,7 +949,6 @@
   "make_private": "",
   "manage_beta_program_membership": "",
   "manage_files_from_your_dropbox_folder": "",
-  "manage_group_managers": "",
   "manage_group_members_subtext": "",
   "manage_group_settings": "",
   "manage_group_settings_subtext": "",
@@ -960,7 +956,6 @@
   "manage_group_settings_subtext_managed_users": "",
   "manage_institution_managers": "",
   "manage_managers_subtext": "",
-  "manage_members": "",
   "manage_newsletter": "",
   "manage_publisher_managers": "",
   "manage_sessions": "",
@@ -1326,7 +1321,6 @@
   "remote_service_error": "",
   "remove": "",
   "remove_access": "",
-  "remove_add_on": "",
   "remove_email_address": "",
   "remove_from_group": "",
   "remove_link": "",
@@ -1634,7 +1628,6 @@
   "sure_you_want_to_leave_group": "",
   "switch_back_to_monthly_pay_20_more": "",
   "switch_compile_mode_for_faster_draft_compilation": "",
-  "switch_plan": "",
   "switch_to_editor": "",
   "switch_to_new_editor": "",
   "switch_to_old_editor": "",
@@ -1892,7 +1885,6 @@
   "untrash": "",
   "update": "",
   "update_account_info": "",
-  "update_billing_details": "",
   "update_dropbox_settings": "",
   "update_your_billing_details": "",
   "updates_to_project_sharing": "",
@@ -2095,7 +2087,6 @@
   "your_new_plan": "",
   "your_password_was_detected": "",
   "your_plan": "",
-  "your_plan_is": "",
   "your_plan_is_changing_at_term_end": "",
   "your_plan_is_limited_to_n_editors": "",
   "your_plan_is_limited_to_n_editors_plural": "",

+ 5 - 19
services/web/frontend/js/features/group-management/components/group-members.tsx

@@ -5,7 +5,6 @@ import getMeta from '../../../utils/meta'
 import { useGroupMembersContext } from '../context/group-members-context'
 import ErrorAlert from './error-alert'
 import MembersList from './members-table/members-list'
-import { useFeatureFlag } from '@/shared/context/split-test-context'
 import { sendMB } from '../../../infrastructure/event-tracking'
 import BackButton from '@/features/group-management/components/back-button'
 import OLRow from '@/features/ui/components/ol/ol-row'
@@ -30,17 +29,12 @@ export default function GroupMembers() {
     paths,
   } = useGroupMembersContext()
   const [emailString, setEmailString] = useState<string>('')
-  const isFlexibleGroupLicensingFeatureFlagEnabled = useFeatureFlag(
-    'flexible-group-licensing'
-  )
 
   const groupId = getMeta('ol-groupId')
   const groupName = getMeta('ol-groupName')
   const groupSize = getMeta('ol-groupSize')
   const canUseFlexibleLicensing = getMeta('ol-canUseFlexibleLicensing')
   const canUseAddSeatsFeature = getMeta('ol-canUseAddSeatsFeature')
-  const isFlexibleGroupLicensing =
-    canUseFlexibleLicensing && isFlexibleGroupLicensingFeatureFlagEnabled
 
   const handleEmailsChange = useCallback(
     e => {
@@ -59,7 +53,7 @@ export default function GroupMembers() {
   }
 
   const groupSizeDetails = () => {
-    if (isFlexibleGroupLicensing) {
+    if (canUseFlexibleLicensing) {
       return (
         <small data-testid="group-size-details">
           <strong>
@@ -145,11 +139,7 @@ export default function GroupMembers() {
                 className="add-more-members-form"
                 data-testid="add-more-members-form"
               >
-                <p className="small">
-                  {isFlexibleGroupLicensing
-                    ? t('invite_more_members')
-                    : t('add_more_members')}
-                </p>
+                <p className="small">{t('invite_more_members')}</p>
                 <ErrorAlert error={inviteError} />
                 <form onSubmit={onAddMembersSubmit}>
                   <OLRow>
@@ -170,19 +160,15 @@ export default function GroupMembers() {
                         bs3Props={{
                           loading: inviteMemberLoading ? (
                             <>
-                              {isFlexibleGroupLicensing
-                                ? t('inviting')
-                                : t('adding')}
+                              {t('inviting')}
                               &hellip;
                             </>
-                          ) : isFlexibleGroupLicensing ? (
-                            t('invite')
                           ) : (
-                            t('add')
+                            t('invite')
                           ),
                         }}
                       >
-                        {isFlexibleGroupLicensing ? t('invite') : t('add')}
+                        {t('invite')}
                       </OLButton>
                     </OLCol>
                     <OLCol xs={2}>

+ 1 - 5
services/web/frontend/js/features/subscription/components/dashboard/group-settings-button.tsx

@@ -1,6 +1,5 @@
 import { RowLink } from '@/features/subscription/components/dashboard/row-link'
 import { useTranslation } from 'react-i18next'
-import { useFeatureFlag } from '@/shared/context/split-test-context'
 import { useLocation } from '@/shared/hooks/use-location'
 import MaterialIcon from '@/shared/components/material-icon'
 import OLTag from '@/features/ui/components/ol/ol-tag'
@@ -36,13 +35,10 @@ function AvailableWithGroupProfessionalBadge() {
 
 function useGroupSettingsButton(subscription: ManagedGroupSubscription) {
   const { t } = useTranslation()
-  const isFlexibleGroupLicensing = useFeatureFlag('flexible-group-licensing')
   const subscriptionHasManagedUsers =
     subscription.features?.managedUsers === true
   const subscriptionHasGroupSSO = subscription.features?.groupSSO === true
-  const heading = isFlexibleGroupLicensing
-    ? t('group_settings')
-    : t('manage_group_settings')
+  const heading = t('group_settings')
 
   let groupSettingRowSubText = ''
   if (subscriptionHasGroupSSO && subscriptionHasManagedUsers) {

+ 3 - 17
services/web/frontend/js/features/subscription/components/dashboard/managed-group-subscriptions.tsx

@@ -8,7 +8,6 @@ import { useSubscriptionDashboardContext } from '../../context/subscription-dash
 import { RowLink } from './row-link'
 import { ManagedGroupSubscription } from '../../../../../../types/subscription/dashboard/subscription'
 import { bsVersion } from '@/features/utils/bootstrap-5'
-import { useFeatureFlag } from '@/shared/context/split-test-context'
 import classnames from 'classnames'
 
 function ManagedGroupAdministrator({
@@ -91,7 +90,6 @@ function ManagedGroupAdministrator({
 export default function ManagedGroupSubscriptions() {
   const { t } = useTranslation()
   const { managedGroupSubscriptions } = useSubscriptionDashboardContext()
-  const isFlexibleGroupLicensing = useFeatureFlag('flexible-group-licensing')
 
   if (!managedGroupSubscriptions) {
     return null
@@ -115,21 +113,13 @@ export default function ManagedGroupSubscriptions() {
             <ul className="list-group p-0">
               <RowLink
                 href={`/manage/groups/${subscription._id}/members`}
-                heading={
-                  isFlexibleGroupLicensing
-                    ? t('group_members')
-                    : t('manage_members')
-                }
+                heading={t('group_members')}
                 subtext={t('manage_group_members_subtext')}
                 icon="groups"
               />
               <RowLink
                 href={`/manage/groups/${subscription._id}/managers`}
-                heading={
-                  isFlexibleGroupLicensing
-                    ? t('group_managers')
-                    : t('manage_group_managers')
-                }
+                heading={t('group_managers')}
                 subtext={t('manage_managers_subtext')}
                 icon="manage_accounts"
               />
@@ -141,11 +131,7 @@ export default function ManagedGroupSubscriptions() {
               )}
               <RowLink
                 href={`/metrics/groups/${subscription._id}`}
-                heading={
-                  isFlexibleGroupLicensing
-                    ? t('usage_metrics')
-                    : t('view_metrics')
-                }
+                heading={t('usage_metrics')}
                 subtext={t('view_metrics_group_subtext')}
                 icon="insights"
               />

+ 1 - 17
services/web/frontend/js/features/subscription/components/dashboard/personal-subscription.tsx

@@ -1,7 +1,5 @@
 import { Trans, useTranslation } from 'react-i18next'
 import { RecurlySubscription } from '../../../../../../types/subscription/dashboard/subscription'
-import { ActiveSubscription } from './states/active/active'
-import { ActiveAiAddonSubscription } from './states/active/active-ai-addon'
 import { PausedSubscription } from './states/active/paused'
 import { ActiveSubscriptionNew } from '@/features/subscription/components/dashboard/states/active/active-new'
 import { CanceledSubscription } from './states/canceled'
@@ -9,8 +7,6 @@ import { ExpiredSubscription } from './states/expired'
 import { useSubscriptionDashboardContext } from '../../context/subscription-dashboard-context'
 import PersonalSubscriptionRecurlySyncEmail from './personal-subscription-recurly-sync-email'
 import OLNotification from '@/features/ui/components/ol/ol-notification'
-import { isStandaloneAiPlanCode, AI_ADD_ON_CODE } from '../../data/add-on-codes'
-import { useFeatureFlag } from '@/shared/context/split-test-context'
 
 function PastDueSubscriptionAlert({
   subscription,
@@ -44,22 +40,10 @@ function PersonalSubscriptionStates({
 }) {
   const { t } = useTranslation()
   const state = subscription?.recurly.state
-  const isFlexibleGroupLicensing = useFeatureFlag('flexible-group-licensing')
 
-  const hasAiAddon = subscription?.addOns?.some(
-    addOn => addOn.addOnCode === AI_ADD_ON_CODE
-  )
-
-  const onAiStandalonePlan = isStandaloneAiPlanCode(subscription.planCode)
-  const planHasAi = onAiStandalonePlan || hasAiAddon
-
-  if (state === 'active' && isFlexibleGroupLicensing) {
+  if (state === 'active') {
     // This version handles subscriptions with and without addons
     return <ActiveSubscriptionNew subscription={subscription} />
-  } else if (state === 'active' && planHasAi) {
-    return <ActiveAiAddonSubscription subscription={subscription} />
-  } else if (state === 'active') {
-    return <ActiveSubscription subscription={subscription} />
   } else if (state === 'canceled') {
     return <CanceledSubscription subscription={subscription} />
   } else if (state === 'expired') {

+ 7 - 20
services/web/frontend/js/features/subscription/components/dashboard/premium-features-link.tsx

@@ -1,28 +1,15 @@
 import { Trans } from 'react-i18next'
-import { useFeatureFlag } from '@/shared/context/split-test-context'
 
 function PremiumFeaturesLink() {
-  const isFlexibleGroupLicensing = useFeatureFlag('flexible-group-licensing')
-
   return (
     <p>
-      {isFlexibleGroupLicensing ? (
-        <Trans
-          i18nKey="get_most_subscription_discover_premium_features"
-          components={[
-            // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
-            <a href="/learn/how-to/Overleaf_premium_features" />,
-          ]}
-        />
-      ) : (
-        <Trans
-          i18nKey="get_most_subscription_by_checking_features"
-          components={[
-            // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
-            <a href="/about/features-overview" />,
-          ]}
-        />
-      )}
+      <Trans
+        i18nKey="get_most_subscription_discover_premium_features"
+        components={[
+          // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
+          <a href="/learn/how-to/Overleaf_premium_features" />,
+        ]}
+      />
     </p>
   )
 }

+ 0 - 221
services/web/frontend/js/features/subscription/components/dashboard/states/active/active-ai-addon.tsx

@@ -1,221 +0,0 @@
-import { useTranslation, Trans } from 'react-i18next'
-import { PriceExceptions } from '../../../shared/price-exceptions'
-import { useSubscriptionDashboardContext } from '../../../../context/subscription-dashboard-context'
-import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
-import { CancelSubscription } from './cancel-plan/cancel-subscription'
-import { PendingPlanChange } from './pending-plan-change'
-import SubscriptionRemainder from './subscription-remainder'
-import { ChangePlanModal } from './change-plan/modals/change-plan-modal'
-import { ConfirmChangePlanModal } from './change-plan/modals/confirm-change-plan-modal'
-import { KeepCurrentPlanModal } from './change-plan/modals/keep-current-plan-modal'
-import { ChangeToGroupModal } from './change-plan/modals/change-to-group-modal'
-import { CancelAiAddOnModal } from './change-plan/modals/cancel-ai-add-on-modal'
-import { PendingRecurlyPlan } from '../../../../../../../../types/subscription/plan'
-import {
-  ADD_ON_NAME,
-  AI_ADD_ON_CODE,
-  isStandaloneAiPlanCode,
-} from '../../../../data/add-on-codes'
-import { CancelSubscriptionButton } from './cancel-subscription-button'
-
-import OLButton from '@/features/ui/components/ol/ol-button'
-
-export function ActiveAiAddonSubscription({
-  subscription,
-}: {
-  subscription: RecurlySubscription
-}) {
-  const { t } = useTranslation()
-  const {
-    recurlyLoadError,
-    showCancellation,
-    setModalIdShown,
-    memberGroupSubscriptions,
-    institutionMemberships,
-  } = useSubscriptionDashboardContext()
-  if (showCancellation) return <CancelSubscription />
-
-  const onStandalonePlan = isStandaloneAiPlanCode(subscription.planCode)
-
-  let planName
-  if (onStandalonePlan) {
-    planName = 'Overleaf Free'
-    if (institutionMemberships && institutionMemberships.length > 0) {
-      planName = 'Overleaf Professional'
-    }
-    if (memberGroupSubscriptions.length > 0) {
-      if (
-        memberGroupSubscriptions.some(s => s.planLevelName === 'Professional')
-      ) {
-        planName = 'Overleaf Professional'
-      } else {
-        planName = 'Overleaf Standard'
-      }
-    }
-  } else {
-    planName = subscription.plan.name
-  }
-
-  const handlePlanChange = () => setModalIdShown('change-plan')
-
-  const handleCancelClick = () => setModalIdShown('cancel-ai-add-on')
-
-  return (
-    <>
-      <p className="mb-0">
-        <Trans
-          i18nKey="your_plan_is"
-          values={{ planName }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-          components={{ strong: <strong /> }}
-        />
-      </p>
-      <p>
-        <Trans
-          i18nKey="add_ons_are"
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-          values={{
-            addOnName: ADD_ON_NAME,
-          }}
-          components={{ strong: <strong /> }}
-        />
-      </p>
-      <p>
-        {subscription.pendingPlan && (
-          <PendingPlanChange subscription={subscription} />
-        )}
-      </p>
-      {subscription.pendingPlan &&
-        subscription.pendingPlan.name !== subscription.plan.name && (
-          <p>{t('want_change_to_apply_before_plan_end')}</p>
-        )}
-      <p>
-        <Trans
-          i18nKey="next_payment_of_x_collectected_on_y"
-          values={{
-            paymentAmmount: subscription.recurly.displayPrice,
-            collectionDate: subscription.recurly.nextPaymentDueDate,
-          }}
-          shouldUnescape
-          tOptions={{ interpolation: { escapeValue: true } }}
-          components={[
-            // eslint-disable-next-line react/jsx-key
-            <strong />,
-            // eslint-disable-next-line react/jsx-key
-            <strong />,
-          ]}
-        />
-      </p>
-      <PriceExceptions subscription={subscription} />
-      {!recurlyLoadError && (
-        <p>
-          <i>
-            <SubscriptionRemainder subscription={subscription} hideTime />
-          </i>
-        </p>
-      )}
-      {!recurlyLoadError && (
-        <p className="d-inline-flex flex-wrap gap-1">
-          {onStandalonePlan ? (
-            <StandaloneAiPlanActions
-              handlePlanChange={handlePlanChange}
-              handleCancelClick={handleCancelClick}
-            />
-          ) : (
-            <PlanWithAddonsActions
-              handlePlanChange={handlePlanChange}
-              handleCancelClick={handleCancelClick}
-              subscription={subscription}
-            />
-          )}
-        </p>
-      )}
-      <p>
-        <a
-          href={subscription.recurly.accountManagementLink}
-          target="_blank"
-          rel="noreferrer noopener"
-        >
-          {t('view_invoices')}
-        </a>
-      </p>
-      <p>
-        <a
-          href={subscription.recurly.billingDetailsLink}
-          target="_blank"
-          rel="noreferrer noopener"
-        >
-          {t('update_billing_details')}
-        </a>
-      </p>
-
-      <ChangePlanModal />
-      <ConfirmChangePlanModal />
-      <KeepCurrentPlanModal />
-      <ChangeToGroupModal />
-      <CancelAiAddOnModal />
-    </>
-  )
-}
-
-function StandaloneAiPlanActions({
-  handlePlanChange,
-  handleCancelClick,
-}: {
-  handlePlanChange(): void
-  handleCancelClick(): void
-}) {
-  const { t } = useTranslation()
-  return (
-    <>
-      <OLButton variant="secondary" onClick={handlePlanChange}>
-        {t('upgrade')}
-      </OLButton>
-      <OLButton variant="danger-ghost" onClick={handleCancelClick}>
-        {t('remove_add_on')}
-      </OLButton>
-    </>
-  )
-}
-
-function PlanWithAddonsActions({
-  handlePlanChange,
-  handleCancelClick,
-  subscription,
-}: {
-  handlePlanChange(): void
-  handleCancelClick(): void
-  subscription: RecurlySubscription
-}) {
-  const { t } = useTranslation()
-
-  const pendingPlan = subscription.pendingPlan as PendingRecurlyPlan
-
-  const hasAiAddon = subscription.addOns?.some(
-    addOn => addOn.addOnCode === AI_ADD_ON_CODE
-  )
-
-  const pendingCancellation = Boolean(
-    hasAiAddon &&
-      pendingPlan &&
-      !pendingPlan.addOns?.some(addOn => addOn.add_on_code === AI_ADD_ON_CODE)
-  )
-  return (
-    <>
-      <OLButton variant="secondary" onClick={handlePlanChange}>
-        {t('switch_plan')}
-      </OLButton>
-
-      <>
-        {!pendingCancellation && (
-          <OLButton variant="danger-ghost" onClick={handleCancelClick}>
-            {t('remove_add_on')}
-          </OLButton>
-        )}
-        <CancelSubscriptionButton />
-      </>
-    </>
-  )
-}

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

@@ -89,13 +89,11 @@
   "add_files": "Add Files",
   "add_more_editors": "Add more editors",
   "add_more_managers": "Add more managers",
-  "add_more_members": "Add more members",
   "add_more_users": "Add more users",
   "add_more_users_to_my_plan": "Add more users to my plan",
   "add_new_email": "Add new email",
   "add_on": "Add-on",
   "add_ons": "Add-ons",
-  "add_ons_are": "<strong>Add-ons:</strong> __addOnName__",
   "add_or_remove_project_from_tag": "Add or remove project from tag __tagName__",
   "add_overleaf_assist": "Add Overleaf Assist",
   "add_overleaf_assist_to_your_group_subscription": "Add Overleaf Assist to your group subscription",
@@ -810,7 +808,6 @@
   "get_in_touch": "Get in touch",
   "get_in_touch_having_problems": "<a href=\"__link__\">Get in touch with support</a> if you’re having problems",
   "get_involved": "Get involved",
-  "get_most_subscription_by_checking_features": "Get the most out of your __appName__ subscription by checking out <0>__appName__’s features</0>.",
   "get_most_subscription_discover_premium_features": "Get the most from your __appName__ subscription. <0>Discover premium features</0>.",
   "get_the_best_overleaf_experience": "Get the best Overleaf experience",
   "get_the_most_out_headline": "Get the most out of __appName__ with features such as:",
@@ -1260,7 +1257,6 @@
   "make_private": "Make Private",
   "manage_beta_program_membership": "Manage Beta Program Membership",
   "manage_files_from_your_dropbox_folder": "Manage files from your Dropbox folder",
-  "manage_group_managers": "Manage group managers",
   "manage_group_members_subtext": "Add or remove members from your group subscription",
   "manage_group_settings": "Manage group settings",
   "manage_group_settings_subtext": "Configure and manage SSO and Managed Users",
@@ -1268,7 +1264,6 @@
   "manage_group_settings_subtext_managed_users": "Turn on Managed Users",
   "manage_institution_managers": "Manage institution managers",
   "manage_managers_subtext": "Assign or remove manager privileges",
-  "manage_members": "Manage members",
   "manage_newsletter": "Manage Your Newsletter Preferences",
   "manage_publisher_managers": "Manage publisher managers",
   "manage_sessions": "Manage Your Sessions",
@@ -1761,7 +1756,6 @@
   "remote_service_error": "The remote service produced an error",
   "remove": "Remove",
   "remove_access": "Remove access",
-  "remove_add_on": "Remove add-on",
   "remove_email_address": "Remove email address",
   "remove_from_group": "Remove from group",
   "remove_link": "Remove link",
@@ -2133,7 +2127,6 @@
   "sv": "Swedish",
   "switch_back_to_monthly_pay_20_more": "Switch back to monthly (20% more)",
   "switch_compile_mode_for_faster_draft_compilation": "Switch compile mode for faster draft compilation",
-  "switch_plan": "Switch plan",
   "switch_to_editor": "Switch to editor",
   "switch_to_new_editor": "Switch to new editor",
   "switch_to_old_editor": "Switch to old editor",
@@ -2429,7 +2422,6 @@
   "untrash": "Restore",
   "update": "Update",
   "update_account_info": "Update Account Info",
-  "update_billing_details": "Update billing details",
   "update_dropbox_settings": "Update Dropbox Settings",
   "update_your_billing_details": "Update your billing details",
   "updates_to_project_sharing": "Updates to project sharing",
@@ -2657,7 +2649,6 @@
   "your_password_has_been_successfully_changed": "Your password has been successfully changed",
   "your_password_was_detected": "Your password is on a <0>public list of known compromised passwords</0>. Keep your account safe by changing your password now.",
   "your_plan": "Your plan",
-  "your_plan_is": "<strong>Your plan:</strong> __planName__",
   "your_plan_is_changing_at_term_end": "Your plan is changing to <0>__pendingPlanName__</0> at the end of the current billing period.",
   "your_plan_is_limited_to_n_editors": "Your plan allows __count__ collaborator with edit access and unlimited viewers.",
   "your_plan_is_limited_to_n_editors_plural": "Your plan allows __count__ collaborators with edit access and unlimited viewers.",

+ 15 - 29
services/web/test/frontend/features/group-management/components/group-members.spec.tsx

@@ -1,7 +1,6 @@
 import GroupMembers from '@/features/group-management/components/group-members'
 import { GroupMembersProvider } from '@/features/group-management/context/group-members-context'
 import { User } from '../../../../../types/group-management/user'
-import { SplitTestProvider } from '@/shared/context/split-test-context'
 
 const GROUP_ID = '777fff777fff'
 const PATHS = {
@@ -14,11 +13,9 @@ const PATHS = {
 describe('GroupMembers', function () {
   function mountGroupMembersProvider() {
     cy.mount(
-      <SplitTestProvider>
-        <GroupMembersProvider>
-          <GroupMembers />
-        </GroupMembersProvider>
-      </SplitTestProvider>
+      <GroupMembersProvider>
+        <GroupMembers />
+      </GroupMembersProvider>
     )
   }
 
@@ -49,11 +46,9 @@ describe('GroupMembers', function () {
       })
 
       cy.mount(
-        <SplitTestProvider>
-          <GroupMembersProvider>
-            <GroupMembers />
-          </GroupMembersProvider>
-        </SplitTestProvider>
+        <GroupMembersProvider>
+          <GroupMembers />
+        </GroupMembersProvider>
       )
     })
 
@@ -517,9 +512,6 @@ describe('GroupMembers', function () {
         win.metaAttributesCache.set('ol-groupId', GROUP_ID)
         win.metaAttributesCache.set('ol-groupName', 'My Awesome Team')
         win.metaAttributesCache.set('ol-groupSize', 10)
-        win.metaAttributesCache.set('ol-splitTestVariants', {
-          'flexible-group-licensing': 'enabled',
-        })
         win.metaAttributesCache.set('ol-canUseFlexibleLicensing', true)
         win.metaAttributesCache.set('ol-canUseAddSeatsFeature', true)
       })
@@ -534,11 +526,9 @@ describe('GroupMembers', function () {
       })
 
       cy.mount(
-        <SplitTestProvider>
-          <GroupMembersProvider>
-            <GroupMembers />
-          </GroupMembersProvider>
-        </SplitTestProvider>
+        <GroupMembersProvider>
+          <GroupMembers />
+        </GroupMembersProvider>
       )
 
       cy.findByTestId('group-size-details').contains(
@@ -556,11 +546,9 @@ describe('GroupMembers', function () {
       })
 
       cy.mount(
-        <SplitTestProvider>
-          <GroupMembersProvider>
-            <GroupMembers />
-          </GroupMembersProvider>
-        </SplitTestProvider>
+        <GroupMembersProvider>
+          <GroupMembers />
+        </GroupMembersProvider>
       )
 
       cy.findByTestId('group-size-details').contains(
@@ -575,11 +563,9 @@ describe('GroupMembers', function () {
       })
 
       cy.mount(
-        <SplitTestProvider>
-          <GroupMembersProvider>
-            <GroupMembers />
-          </GroupMembersProvider>
-        </SplitTestProvider>
+        <GroupMembersProvider>
+          <GroupMembers />
+        </GroupMembersProvider>
       )
 
       cy.findByTestId('group-size-details').within(() => {

+ 3 - 3
services/web/test/frontend/features/subscription/components/dashboard/managed-group-subscriptions.test.tsx

@@ -190,7 +190,7 @@ describe('<ManagedGroupSubscriptions />', function () {
         },
       ],
     })
-    await screen.findAllByText('Manage group settings')
+    await screen.findAllByText('Group settings')
     await screen.findAllByText('Configure and manage SSO and Managed Users')
   })
 
@@ -207,7 +207,7 @@ describe('<ManagedGroupSubscriptions />', function () {
         },
       ],
     })
-    await screen.findAllByText('Manage group settings')
+    await screen.findAllByText('Group settings')
     await screen.findAllByText('Turn on Managed Users')
     expect(screen.queryByText('Configure and manage SSO and Managed Users')).to
       .not.exist
@@ -227,7 +227,7 @@ describe('<ManagedGroupSubscriptions />', function () {
         },
       ],
     })
-    await screen.findAllByText('Manage group settings')
+    await screen.findAllByText('Group settings')
     await screen.findAllByText('Configure and manage SSO')
     expect(screen.queryByText('Turn on Managed Users')).to.not.exist
     expect(screen.queryByText('Configure and manage SSO and Managed Users')).to

+ 1 - 1
services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx

@@ -72,7 +72,7 @@ describe('<PersonalSubscription />', function () {
         ],
       })
 
-      screen.getByText('You are currently subscribed to the', { exact: false })
+      screen.getByRole('heading', { name: /billing/i })
     })
 
     it('renders the canceled dash', function () {

+ 2 - 5
services/web/test/frontend/features/subscription/components/dashboard/subscription-dashboard.test.tsx

@@ -31,11 +31,8 @@ describe('<SubscriptionDashboard />', function () {
       })
     })
 
-    it('renders the "Get the most out of your" subscription text', function () {
-      screen.getByText(
-        'Get the most out of your Overleaf subscription by checking out',
-        { exact: false }
-      )
+    it('renders the "Get the most from your subscription" text', function () {
+      screen.getByText(/get the most from your Overleaf subscription/i)
     })
   })
 

+ 6 - 4
services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx

@@ -36,8 +36,8 @@ describe('successful subscription page', function () {
     screen.getByText(
       /it’s support from people like yourself that allows .* to continue to grow and improve/i
     )
-    expect(screen.getByText(/get the most out of your/i).textContent).to.match(
-      /get the most out of your .* subscription by checking out .*’s features/i
+    expect(screen.getByText(/get the most from your/i).textContent).to.match(
+      /get the most from your .* subscription\. discover premium features/i
     )
     expect(
       screen
@@ -66,9 +66,11 @@ describe('successful subscription page', function () {
     )
 
     const helpLink = screen.getByRole('link', {
-      name: /.*’s features/i,
+      name: /discover premium features/i,
     })
-    expect(helpLink.getAttribute('href')).to.equal('/about/features-overview')
+    expect(helpLink.getAttribute('href')).to.equal(
+      '/learn/how-to/Overleaf_premium_features'
+    )
 
     const backToYourProjectsLink = screen.getByRole('link', {
       name: /back to your projects/i,

+ 0 - 27
services/web/test/unit/src/Subscription/SubscriptionGroupControllerTests.mjs

@@ -615,33 +615,6 @@ describe('SubscriptionGroupController', function () {
     })
   })
 
-  describe('flexibleLicensingSplitTest', function () {
-    it('passes when the variant is "enabled"', function (done) {
-      const res = sinon.stub()
-      const next = () => {
-        this.ErrorController.notFound.notCalled.should.equal(true)
-        done()
-      }
-      this.SplitTestHandler.promises.getAssignment.resolves({
-        variant: 'enabled',
-      })
-      this.Controller.flexibleLicensingSplitTest(this.req, res, next, done)
-    })
-
-    it('returns error page when the variant is "default"', function (done) {
-      const res = sinon.stub()
-      const next = sinon.stub()
-      this.ErrorController.notFound = sinon.stub().callsFake(() => {
-        next.notCalled.should.equal(true)
-        done()
-      })
-      this.SplitTestHandler.promises.getAssignment.resolves({
-        variant: 'default',
-      })
-      this.Controller.flexibleLicensingSplitTest(this.req, res, next, done)
-    })
-  })
-
   describe('subscriptionUpgradePage', function () {
     it('should render "subscription upgrade" page', function (done) {
       const olSubscription = { membersLimit: 1, teamName: 'test team' }