diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 7ba5f47b3f..36b576916e 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -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": "", diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/add-ons.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/add-ons.tsx index befbcebaa5..226ed7862e 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/add-ons.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/add-ons.tsx @@ -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 ( +
+
+ +
+
+
{resolveAddOnName(addOnCode)}
+
+ {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 })} +
+
+ {!pendingCancellation && ( +
+ + + + + + handleCancelClick(addOnCode)}> + {t('cancel')} + + + + } + bs5={ + + + + + + handleCancelClick(addOnCode)} + as="button" + tabIndex={-1} + variant="danger" + > + {t('cancel')} + + + + } + /> +
+ )} +
+ ) +} + 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 ( <>

{t('add_ons')}

- {addOnsDisplayPricesEntries.length > 0 ? ( - addOnsDisplayPricesEntries.map(([code, displayPrice]) => ( -
-
- -
-
-
{resolveAddOnName(code)}
-
- {subscription.plan.annual - ? t('x_price_per_year', { price: displayPrice }) - : t('x_price_per_month', { price: displayPrice })} -
-
- {!pendingCancellation && ( -
- - - - - - handleCancelClick(code)}> - {t('cancel')} - - - - } - bs5={ - - - - - - handleCancelClick(code)} - as="button" - tabIndex={-1} - variant="danger" - > - {t('cancel')} - - - - } - /> -
- )} -
+ {addOnsToDisplay && addOnsToDisplay.length > 0 ? ( + addOnsToDisplay.map(addOn => ( + pendingAddOn.addOnCode !== addOn.addOnCode + ) + } + displayPrice={addOnsDisplayPrices[addOn.addOnCode]} + nextBillingDate={subscription.recurly.nextPaymentDueDate} + /> )) ) : (

{t('you_dont_have_any_add_ons_on_your_account')}

diff --git a/services/web/locales/en.json b/services/web/locales/en.json index af241adff3..9c8b14297d 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -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.", "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__ 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",