mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-05 07:09:02 +02:00
Merge pull request #23488 from overleaf/ls-display-add-on-with-pending-cancellation
Display addOn that has pending cancellation GitOrigin-RevId: 5fde493d1b706a1708e0cb4a2de6a7682fb1d1e0
This commit is contained in:
@@ -2005,6 +2005,7 @@
|
||||
"youll_no_longer_need_to_remember_credentials": "",
|
||||
"your_account_is_managed_by_admin_cant_join_additional_group": "",
|
||||
"your_account_is_managed_by_your_group_admin": "",
|
||||
"your_add_on_has_been_cancelled_and_will_remain_active_until_your_billing_cycle_ends_on": "",
|
||||
"your_affiliation_is_confirmed": "",
|
||||
"your_browser_does_not_support_this_feature": "",
|
||||
"your_compile_timed_out": "",
|
||||
|
||||
+128
-97
@@ -18,7 +18,7 @@ import sparkle from '@/shared/svgs/sparkle.svg'
|
||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||
import classnames from 'classnames'
|
||||
import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
|
||||
import { PendingRecurlyPlan } from '../../../../../../../../types/subscription/plan'
|
||||
import { LICENSE_ADD_ON } from '@/features/group-management/components/upgrade-subscription/upgrade-subscription-plan-details'
|
||||
|
||||
type AddOnsProps = {
|
||||
subscription: RecurlySubscription
|
||||
@@ -26,114 +26,145 @@ type AddOnsProps = {
|
||||
handleCancelClick: (code: string) => void
|
||||
}
|
||||
|
||||
type AddOnProps = {
|
||||
addOnCode: string
|
||||
displayPrice: string | undefined
|
||||
pendingCancellation: boolean
|
||||
isAnnual: boolean
|
||||
handleCancelClick: (code: string) => void
|
||||
nextBillingDate: string
|
||||
}
|
||||
|
||||
function resolveAddOnName(addOnCode: string) {
|
||||
switch (addOnCode) {
|
||||
case AI_ADD_ON_CODE:
|
||||
case AI_STANDALONE_ANNUAL_PLAN_CODE:
|
||||
case AI_STANDALONE_PLAN_CODE:
|
||||
return ADD_ON_NAME
|
||||
}
|
||||
}
|
||||
|
||||
function AddOn({
|
||||
addOnCode,
|
||||
displayPrice,
|
||||
pendingCancellation,
|
||||
isAnnual,
|
||||
handleCancelClick,
|
||||
nextBillingDate,
|
||||
}: AddOnProps) {
|
||||
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">{resolveAddOnName(addOnCode)}</div>
|
||||
<div className="description small mt-1">
|
||||
{pendingCancellation
|
||||
? t(
|
||||
'your_add_on_has_been_cancelled_and_will_remain_active_until_your_billing_cycle_ends_on',
|
||||
{ nextBillingDate }
|
||||
)
|
||||
: isAnnual
|
||||
? t('x_price_per_year', { price: displayPrice })
|
||||
: t('x_price_per_month', { price: displayPrice })}
|
||||
</div>
|
||||
</div>
|
||||
{!pendingCancellation && (
|
||||
<div className="ms-auto">
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<ControlledDropdown id="add-ons-actions" pullRight>
|
||||
<BS3Dropdown.Toggle
|
||||
noCaret
|
||||
bsStyle={null}
|
||||
className="add-on-options-toggle btn-secondary"
|
||||
>
|
||||
<MaterialIcon
|
||||
type="more_vert"
|
||||
accessibilityLabel={t('more_options')}
|
||||
/>
|
||||
</BS3Dropdown.Toggle>
|
||||
<BS3Dropdown.Menu>
|
||||
<BS3MenuItem onClick={() => handleCancelClick(addOnCode)}>
|
||||
{t('cancel')}
|
||||
</BS3MenuItem>
|
||||
</BS3Dropdown.Menu>
|
||||
</ControlledDropdown>
|
||||
}
|
||||
bs5={
|
||||
<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
|
||||
onClick={() => handleCancelClick(addOnCode)}
|
||||
as="button"
|
||||
tabIndex={-1}
|
||||
variant="danger"
|
||||
>
|
||||
{t('cancel')}
|
||||
</OLDropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AddOns({
|
||||
subscription,
|
||||
onStandalonePlan,
|
||||
handleCancelClick,
|
||||
}: AddOnsProps) {
|
||||
const { t } = useTranslation()
|
||||
const addOnsDisplayPrices =
|
||||
subscription.recurly.addOnDisplayPricesWithoutAdditionalLicense
|
||||
const addOnsDisplayPricesEntries = Object.entries(
|
||||
onStandalonePlan
|
||||
? {
|
||||
[AI_STANDALONE_PLAN_CODE]: subscription.recurly.displayPrice,
|
||||
}
|
||||
: addOnsDisplayPrices
|
||||
)
|
||||
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)
|
||||
)
|
||||
|
||||
const resolveAddOnName = (addOnCode: string) => {
|
||||
switch (addOnCode) {
|
||||
case AI_ADD_ON_CODE:
|
||||
case AI_STANDALONE_ANNUAL_PLAN_CODE:
|
||||
case AI_STANDALONE_PLAN_CODE:
|
||||
return ADD_ON_NAME
|
||||
}
|
||||
}
|
||||
const addOnsDisplayPrices = onStandalonePlan
|
||||
? {
|
||||
[AI_STANDALONE_PLAN_CODE]: subscription.recurly.displayPrice,
|
||||
}
|
||||
: subscription.recurly.addOnDisplayPricesWithoutAdditionalLicense
|
||||
const addOnsToDisplay = onStandalonePlan
|
||||
? [{ addOnCode: AI_STANDALONE_PLAN_CODE }]
|
||||
: subscription.addOns?.filter(addOn => addOn.addOnCode !== LICENSE_ADD_ON)
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className={classnames('h3', bsVersion({ bs5: 'fw-bold' }))}>
|
||||
{t('add_ons')}
|
||||
</h2>
|
||||
{addOnsDisplayPricesEntries.length > 0 ? (
|
||||
addOnsDisplayPricesEntries.map(([code, displayPrice]) => (
|
||||
<div className="add-on-card" key={code}>
|
||||
<div>
|
||||
<img
|
||||
alt="sparkle"
|
||||
className="add-on-card-icon"
|
||||
src={sparkle}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="add-on-card-content">
|
||||
<div className="heading">{resolveAddOnName(code)}</div>
|
||||
<div className="description small mt-1">
|
||||
{subscription.plan.annual
|
||||
? t('x_price_per_year', { price: displayPrice })
|
||||
: t('x_price_per_month', { price: displayPrice })}
|
||||
</div>
|
||||
</div>
|
||||
{!pendingCancellation && (
|
||||
<div className="ms-auto">
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<ControlledDropdown id="add-ons-actions" pullRight>
|
||||
<BS3Dropdown.Toggle
|
||||
noCaret
|
||||
bsStyle={null}
|
||||
className="add-on-options-toggle btn-secondary"
|
||||
>
|
||||
<MaterialIcon
|
||||
type="more_vert"
|
||||
accessibilityLabel={t('more_options')}
|
||||
/>
|
||||
</BS3Dropdown.Toggle>
|
||||
<BS3Dropdown.Menu>
|
||||
<BS3MenuItem onClick={() => handleCancelClick(code)}>
|
||||
{t('cancel')}
|
||||
</BS3MenuItem>
|
||||
</BS3Dropdown.Menu>
|
||||
</ControlledDropdown>
|
||||
}
|
||||
bs5={
|
||||
<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
|
||||
onClick={() => handleCancelClick(code)}
|
||||
as="button"
|
||||
tabIndex={-1}
|
||||
variant="danger"
|
||||
>
|
||||
{t('cancel')}
|
||||
</OLDropdownMenuItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{addOnsToDisplay && addOnsToDisplay.length > 0 ? (
|
||||
addOnsToDisplay.map(addOn => (
|
||||
<AddOn
|
||||
addOnCode={addOn.addOnCode}
|
||||
key={addOn.addOnCode}
|
||||
isAnnual={Boolean(subscription.plan.annual)}
|
||||
handleCancelClick={handleCancelClick}
|
||||
pendingCancellation={
|
||||
subscription.pendingPlan !== undefined &&
|
||||
(subscription.pendingPlan.addOns ?? []).every(
|
||||
pendingAddOn => pendingAddOn.addOnCode !== addOn.addOnCode
|
||||
)
|
||||
}
|
||||
displayPrice={addOnsDisplayPrices[addOn.addOnCode]}
|
||||
nextBillingDate={subscription.recurly.nextPaymentDueDate}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<p>{t('you_dont_have_any_add_ons_on_your_account')}</p>
|
||||
|
||||
@@ -2567,6 +2567,7 @@
|
||||
"your_account_is_managed_by_admin_cant_join_additional_group": "Your __appName__ account is managed by your current group admin (__admin__). This means you can’t join additional group subscriptions. <0>Read more about Managed Users.</0>",
|
||||
"your_account_is_managed_by_your_group_admin": "Your account is managed by your group admin. You can’t change or delete your email address.",
|
||||
"your_account_is_suspended": "Your account is suspended",
|
||||
"your_add_on_has_been_cancelled_and_will_remain_active_until_your_billing_cycle_ends_on": "Your add-on has been cancelled and will remain active until your billing cycle ends on __nextBillingDate__",
|
||||
"your_affiliation_is_confirmed": "Your <0>__institutionName__</0> affiliation is confirmed.",
|
||||
"your_browser_does_not_support_this_feature": "Sorry, your browser doesn’t support this feature. Please update your browser to its latest version.",
|
||||
"your_compile_timed_out": "Your compile timed out",
|
||||
|
||||
Reference in New Issue
Block a user