mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-01 21:31:36 +02:00
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:
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
),
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
| .
|
||||
|
||||
@@ -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,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')}
|
||||
…
|
||||
</>
|
||||
) : isFlexibleGroupLicensing ? (
|
||||
t('invite')
|
||||
) : (
|
||||
t('add')
|
||||
t('invite')
|
||||
),
|
||||
}}
|
||||
>
|
||||
{isFlexibleGroupLicensing ? t('invite') : t('add')}
|
||||
{t('invite')}
|
||||
</OLButton>
|
||||
</OLCol>
|
||||
<OLCol xs={2}>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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') {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 />
|
||||
</>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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.",
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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' }
|
||||
|
||||
Reference in New Issue
Block a user