Merge pull request #25251 from overleaf/jdt-show-wf-provided-bundle-on-free-plans

Show AI Assist entitlment via Writefull on free user's subscriptions page

GitOrigin-RevId: 20a456a231f60df279b949057972125735166904
This commit is contained in:
Jimmy Domagala-Tang
2025-05-07 10:13:35 -04:00
committed by Copybot
parent 54505437d4
commit a4cfecb2aa
6 changed files with 105 additions and 79 deletions

View File

@@ -1,10 +1,15 @@
import { useTranslation, Trans } from 'react-i18next'
import WritefullManagedBundleAddOn from '@/features/subscription/components/dashboard/states/active/change-plan/modals/writefull-bundle-management-modal'
import RedirectAlerts from './redirect-alerts'
import getMeta from '@/utils/meta'
function FreePlan() {
const { t } = useTranslation()
const hasAiAssistViaWritefull = getMeta('ol-hasAiAssistViaWritefull')
return (
<>
<RedirectAlerts />
<Trans
i18nKey="on_free_plan_upgrade_to_access_features"
components={[
@@ -26,6 +31,12 @@ function FreePlan() {
<a className="btn btn-primary me-1" href="/user/subscription/plans">
{t('upgrade_now')}
</a>
{hasAiAssistViaWritefull && (
<>
<h2 className="h3 fw-bold">{t('add_ons')}</h2>
<WritefullManagedBundleAddOn />
</>
)}
</>
)
}

View File

@@ -7,6 +7,7 @@ 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 RedirectAlerts from './redirect-alerts'
function PastDueSubscriptionAlert({
subscription,
@@ -33,27 +34,6 @@ function PastDueSubscriptionAlert({
)
}
function RedirectAlerts() {
const queryParams = new URLSearchParams(window.location.search)
const redirectReason = queryParams.get('redirect-reason')
const { t } = useTranslation()
if (!redirectReason) {
return null
}
let warning
if (redirectReason === 'writefull-entitled') {
warning = t('good_news_you_are_already_receiving_this_add_on_via_writefull')
} else if (redirectReason === 'double-buy') {
warning = t('good_news_you_already_purchased_this_add_on')
} else {
return null
}
return <OLNotification type="warning" content={<>{warning}</>} />
}
function PersonalSubscriptionStates({
subscription,
}: {

View File

@@ -0,0 +1,24 @@
import { useTranslation } from 'react-i18next'
import OLNotification from '@/features/ui/components/ol/ol-notification'
export function RedirectAlerts() {
const queryParams = new URLSearchParams(window.location.search)
const redirectReason = queryParams.get('redirect-reason')
const { t } = useTranslation()
if (!redirectReason) {
return null
}
let warning
if (redirectReason === 'writefull-entitled') {
warning = t('good_news_you_are_already_receiving_this_add_on_via_writefull')
} else if (redirectReason === 'double-buy') {
warning = t('good_news_you_already_purchased_this_add_on')
} else {
return null
}
return <OLNotification type="warning" content={<>{warning}</>} />
}
export default RedirectAlerts

View File

@@ -10,7 +10,6 @@ import { ConfirmChangePlanModal } from './change-plan/modals/confirm-change-plan
import { KeepCurrentPlanModal } from './change-plan/modals/keep-current-plan-modal'
import { ChangeToGroupModal } from './change-plan/modals/change-to-group-modal'
import { CancelAiAddOnModal } from '@/features/subscription/components/dashboard/states/active/change-plan/modals/cancel-ai-add-on-modal'
import { WritefullBundleManagementModal } from '@/features/subscription/components/dashboard/states/active/change-plan/modals/writefull-bundle-management-modal'
import OLButton from '@/features/ui/components/ol/ol-button'
import isInFreeTrial from '../../../../util/is-in-free-trial'
import AddOns from '@/features/subscription/components/dashboard/states/active/add-ons'
@@ -72,7 +71,6 @@ export function ActiveSubscriptionNew({
}
const handlePlanChange = () => setModalIdShown('change-plan')
const handleManageOnWritefull = () => setModalIdShown('manage-on-writefull')
const handleCancelClick = (addOnCode: string) => {
if (
[AI_ASSIST_STANDALONE_MONTHLY_PLAN_CODE, AI_ADD_ON_CODE].includes(
@@ -266,7 +264,6 @@ export function ActiveSubscriptionNew({
subscription={subscription}
onStandalonePlan={onStandalonePlan}
handleCancelClick={handleCancelClick}
handleManageOnWritefull={handleManageOnWritefull}
/>
<ChangePlanModal />
@@ -274,7 +271,6 @@ export function ActiveSubscriptionNew({
<KeepCurrentPlanModal />
<ChangeToGroupModal />
<CancelAiAddOnModal />
<WritefullBundleManagementModal />
<PauseSubscriptionModal />
</>
)

View File

@@ -12,12 +12,12 @@ import {
import sparkle from '@/shared/svgs/sparkle.svg'
import { PaidSubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
import { LICENSE_ADD_ON } from '@/features/group-management/components/upgrade-subscription/upgrade-subscription-plan-details'
import WritefullManagedBundleAddOn from './change-plan/modals/writefull-bundle-management-modal'
type AddOnsProps = {
subscription: PaidSubscription
onStandalonePlan: boolean
handleCancelClick: (code: string) => void
handleManageOnWritefull: () => void
}
type AddOnProps = {
@@ -100,57 +100,10 @@ function AddOn({
)
}
function WritefullGrantedAddOn({
handleManageOnWritefull,
}: {
handleManageOnWritefull: () => void
}) {
const { t } = useTranslation()
return (
<div className="add-on-card">
<div>
<img
alt="sparkle"
className="add-on-card-icon"
src={sparkle}
aria-hidden="true"
/>
</div>
<div className="add-on-card-content">
<div className="heading">{ADD_ON_NAME}</div>
<div className="description small mt-1">
{t('included_as_part_of_your_writefull_subscription')}
</div>
</div>
<div className="ms-auto">
<Dropdown align="end">
<DropdownToggle
id="add-on-dropdown-toggle"
className="add-on-options-toggle"
variant="secondary"
>
<MaterialIcon
type="more_vert"
accessibilityLabel={t('more_options')}
/>
</DropdownToggle>
<DropdownMenu flip={false}>
<OLDropdownMenuItem tabIndex={-1} onClick={handleManageOnWritefull}>
{t('manage_subscription')}
</OLDropdownMenuItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
)
}
function AddOns({
subscription,
onStandalonePlan,
handleCancelClick,
handleManageOnWritefull,
}: AddOnsProps) {
const { t } = useTranslation()
const hasAiAssistViaWritefull = getMeta('ol-hasAiAssistViaWritefull')
@@ -187,11 +140,7 @@ function AddOns({
nextBillingDate={subscription.payment.nextPaymentDueDate}
/>
))}
{hasAiAssistViaWritefull && (
<WritefullGrantedAddOn
handleManageOnWritefull={handleManageOnWritefull}
/>
)}
{hasAiAssistViaWritefull && <WritefullManagedBundleAddOn />}
</>
) : (
<p>{t('you_dont_have_any_add_ons_on_your_account')}</p>

View File

@@ -8,8 +8,13 @@ import OLModal, {
OLModalTitle,
} from '@/features/ui/components/ol/ol-modal'
import OLButton from '@/features/ui/components/ol/ol-button'
import sparkle from '@/shared/svgs/sparkle.svg'
import { Dropdown, DropdownMenu, DropdownToggle } from 'react-bootstrap-5'
import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item'
import MaterialIcon from '@/shared/components/material-icon'
import { ADD_ON_NAME } from '@/features/subscription/data/add-on-codes'
export function WritefullBundleManagementModal() {
function WritefullBundleManagementModal() {
const modalId: SubscriptionDashModalIds = 'manage-on-writefull'
const { t } = useTranslation()
const { handleCloseModal, modalIdShown } = useSubscriptionDashboardContext()
@@ -49,3 +54,64 @@ export function WritefullBundleManagementModal() {
</OLModal>
)
}
function WritefullGrantedAddOn({
handleManageOnWritefull,
}: {
handleManageOnWritefull: () => void
}) {
const { t } = useTranslation()
return (
<div className="add-on-card">
<div>
<img
alt="sparkle"
className="add-on-card-icon"
src={sparkle}
aria-hidden="true"
/>
</div>
<div className="add-on-card-content">
<div className="heading">{ADD_ON_NAME}</div>
<div className="description small mt-1">
{t('included_as_part_of_your_writefull_subscription')}
</div>
</div>
<div className="ms-auto">
<Dropdown align="end">
<DropdownToggle
id="add-on-dropdown-toggle"
className="add-on-options-toggle"
variant="secondary"
>
<MaterialIcon
type="more_vert"
accessibilityLabel={t('more_options')}
/>
</DropdownToggle>
<DropdownMenu flip={false}>
<OLDropdownMenuItem tabIndex={-1} onClick={handleManageOnWritefull}>
{t('manage_subscription')}
</OLDropdownMenuItem>
</DropdownMenu>
</Dropdown>
</div>
</div>
)
}
export function WritefullManagedBundleAddOn() {
const { setModalIdShown } = useSubscriptionDashboardContext()
const handleManageOnWritefull = () => setModalIdShown('manage-on-writefull')
return (
<>
<WritefullGrantedAddOn
handleManageOnWritefull={handleManageOnWritefull}
/>
<WritefullBundleManagementModal />
</>
)
}
export default WritefullManagedBundleAddOn