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
This commit is contained in:
ilkin-overleaf
2025-03-28 12:04:41 +02:00
committed by Copybot
parent f0fe0db10c
commit 61ce012fb5
19 changed files with 94 additions and 484 deletions

View File

@@ -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
)
}

View File

@@ -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
),

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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 youll 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
| .

View File

@@ -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": "",

View File

@@ -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}>

View File

@@ -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) {

View File

@@ -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"
/>

View File

@@ -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') {

View File

@@ -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>
)
}

View File

@@ -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 />
</>
</>
)
}

View File

@@ -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 youre 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.",

View File

@@ -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(() => {

View File

@@ -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

View File

@@ -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 () {

View File

@@ -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)
})
})

View File

@@ -36,8 +36,8 @@ describe('successful subscription page', function () {
screen.getByText(
/its 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,

View File

@@ -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' }