Skip to content

Commit

Permalink
refactor(schemas, console): remove deprecated ReservedPlanIds (#6820)
Browse files Browse the repository at this point in the history
remove deprecated ReservedPlanIds and refactor the skuId usage in console
  • Loading branch information
simeng-li authored Nov 25, 2024
1 parent 3e034b5 commit 4b5db6e
Show file tree
Hide file tree
Showing 18 changed files with 55 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props)
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('paywall.applications', { count: currentSubscriptionQuota.applicationsLimit ?? 0 })}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function Footer({ isCreatingSocialConnector, isCreateButtonDisabled, onClickCrea
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('social_connectors', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function SkuCardItem({ sku, onSelect, buttonProps }: Props) {
</div>
</div>
<div className={styles.description}>
<PlanDescription skuId={skuId} planId={skuId} />
<PlanDescription skuId={skuId} />
</div>
</div>
<div className={styles.content}>
Expand Down
15 changes: 8 additions & 7 deletions packages/console/src/components/MauExceededModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import styles from './index.module.scss';

function MauExceededModal() {
const {
currentSubscription: { planId, isEnterprisePlan },
currentSubscription: { planId },
} = useContext(SubscriptionDataContext);
const { currentTenant } = useContext(TenantsContext);

Expand All @@ -37,11 +37,12 @@ function MauExceededModal() {
return null;
}

const isMauExceeded =
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain, prettier/prettier
cond(currentTenant && currentTenant.quota.mauLimit !== null &&
currentTenant.usage.activeUsers >= currentTenant.quota.mauLimit
);
const isMauExceeded = cond(
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain
currentTenant &&
currentTenant.quota.mauLimit !== null &&
currentTenant.usage.activeUsers >= currentTenant.quota.mauLimit
);

if (!isMauExceeded) {
return null;
Expand Down Expand Up @@ -77,7 +78,7 @@ function MauExceededModal() {
<InlineNotification severity="error">
<Trans
components={{
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('upsell.mau_exceeded_modal.notification')}
Expand Down
25 changes: 15 additions & 10 deletions packages/console/src/components/PlanDescription/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ReservedPlanId } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { type TFuncKey } from 'i18next';

import DynamicT from '@/ds-components/DynamicT';
Expand All @@ -10,20 +9,26 @@ const registeredPlanDescriptionPhrasesMap: Record<
> = {
[ReservedPlanId.Free]: 'free_plan_description',
[ReservedPlanId.Pro]: 'pro_plan_description',
[ReservedPlanId.Enterprise]: 'enterprise_description',
};

const getRegisteredPlanDescriptionPhrase = (
skuId: string,
isEnterprisePlan = false
): TFuncKey<'translation', 'admin_console.subscription'> | undefined => {
if (isEnterprisePlan) {
return 'enterprise_description';
}

return registeredPlanDescriptionPhrasesMap[skuId];
};

type Props = {
/** Temporarily mark as optional. */
readonly skuId?: string;
/** @deprecated */
readonly planId: string;
readonly skuId: string;
readonly isEnterprisePlan?: boolean;
};

function PlanDescription({ skuId, planId }: Props) {
const description =
conditional(skuId && registeredPlanDescriptionPhrasesMap[skuId]) ??
registeredPlanDescriptionPhrasesMap[planId];
function PlanDescription({ skuId, isEnterprisePlan = false }: Props) {
const description = getRegisteredPlanDescriptionPhrase(skuId, isEnterprisePlan);

if (!description) {
return null;
Expand Down
39 changes: 17 additions & 22 deletions packages/console/src/components/SkuName/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,32 @@ import { ReservedPlanId } from '@logto/schemas';
import { type TFuncKey } from 'i18next';
import { useTranslation } from 'react-i18next';

import { ReservedSkuId } from '@/types/subscriptions';

const registeredSkuIdNamePhraseMap: Record<
const registeredPlanNamePhraseMap: Record<
string,
TFuncKey<'translation', 'admin_console.subscription'> | undefined
TFuncKey<'translation', 'admin_console.subscription'>
> = {
quotaKey: undefined,
[ReservedSkuId.Free]: 'free_plan',
[ReservedSkuId.Pro]: 'pro_plan',
[ReservedSkuId.Development]: 'dev_plan',
[ReservedSkuId.Admin]: 'admin_plan',
[ReservedSkuId.Enterprise]: 'enterprise',
[ReservedPlanId.Free]: 'free_plan',
[ReservedPlanId.Pro]: 'pro_plan',
[ReservedPlanId.Development]: 'dev_plan',
[ReservedPlanId.Admin]: 'admin_plan',
} satisfies Record<ReservedPlanId, TFuncKey<'translation', 'admin_console.subscription'>>;

const getRegisteredSkuNamePhrase = (
skuId: string
): TFuncKey<'translation', 'admin_console.subscription'> => {
const reservedSkuNamePhrase = registeredPlanNamePhraseMap[skuId];

return reservedSkuNamePhrase ?? 'enterprise';
};

type Props = {
readonly skuId: string;
readonly isEnterprisePlan?: boolean;
};

function SkuName({ skuId: rawSkuId, isEnterprisePlan = false }: Props) {
function SkuName({ skuId }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.subscription' });
const skuId = isEnterprisePlan ? ReservedPlanId.Enterprise : rawSkuId;

const skuNamePhrase = registeredSkuIdNamePhraseMap[skuId];

/**
* Note: fallback to the plan name if the phrase is not registered.
*/
const skuName = skuNamePhrase ? String(t(skuNamePhrase)) : skuId;

return <span>{skuName}</span>;
const skuNamePhrase = getRegisteredSkuNamePhrase(skuId);
return <span>{String(t(skuNamePhrase))}</span>;
}

export default SkuName;
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ function TenantDropdownItem({ tenantData, isSelected, onClick }: Props) {
<span>{regionName}</span>
</div>
<span>{t(`tenants.full_env_tag.${tag}`)}</span>
{tag !== TenantTag.Development && (
<SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />
)}
{tag !== TenantTag.Development && <SkuName skuId={planId} />}
</div>
</div>
<Tick className={classNames(styles.checkIcon, isSelected && styles.visible)} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('upsell.paywall.scopes_per_resource', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function Footer({ isCreationLoading, onClickCreate }: Props) {
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('upsell.paywall.resources', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ function ProtectedAppForm({
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('upsell.paywall.applications', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function AssignPermissionsModal({ roleId, roleType, onClose }: Props) {
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('upsell.paywall.scopes_per_role', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function Footer({ roleType, scopes, isCreating, onClickCreate }: Props) {
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{/* User roles limit paywall */}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ReservedPlanId } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import dayjs from 'dayjs';
import { useCallback, useContext, useEffect, useMemo } from 'react';
Expand Down Expand Up @@ -65,19 +64,7 @@ function BillingHistory() {
{
title: <DynamicT forKey="subscription.billing_history.invoice_column" />,
dataIndex: 'planName',
render: ({ skuId: rawSkuId, periodStart, periodEnd }) => {
/**
* @remarks
* The `skuId` should be either ReservedPlanId.Dev, ReservedPlanId.Pro, ReservedPlanId.Admin, ReservedPlanId.Free, or a random string.
* Except for the random string, which corresponds to the custom enterprise plan, other `skuId` values correspond to specific Reserved Plans.
*/
const skuId =
rawSkuId &&
// eslint-disable-next-line no-restricted-syntax
(Object.values(ReservedPlanId).includes(rawSkuId as ReservedPlanId)
? rawSkuId
: ReservedPlanId.Enterprise);

render: ({ skuId, periodStart, periodEnd }) => {
return (
<ItemPreview
title={formatPeriod({ periodStart, periodEnd, displayYear: true })}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ReservedPlanId } from '@logto/schemas';
import { cond } from '@silverhand/essentials';
import { useContext, useMemo } from 'react';

Expand All @@ -24,7 +23,7 @@ type Props = {

function CurrentPlan({ periodicUsage: rawPeriodicUsage }: Props) {
const {
currentSku: { id, unitPrice },
currentSku: { unitPrice },
currentSubscription: { upcomingInvoice, isEnterprisePlan, planId },
} = useContext(SubscriptionDataContext);
const { currentTenant } = useContext(TenantsContext);
Expand All @@ -41,8 +40,6 @@ function CurrentPlan({ periodicUsage: rawPeriodicUsage }: Props) {
[currentTenant, rawPeriodicUsage]
);

const currentSkuId = isEnterprisePlan ? ReservedPlanId.Enterprise : id;

/**
* After the new pricing model goes live, `upcomingInvoice` will always exist. `upcomingInvoice` is updated more frequently than `currentSubscription.upcomingInvoice`.
* However, for compatibility reasons, the price of the SKU's corresponding `unitPrice` will be used as a fallback when it does not exist. If `unitPrice` also does not exist, it means that the tenant does not have any applicable paid subscription, and the bill will be 0.
Expand All @@ -60,10 +57,10 @@ function CurrentPlan({ periodicUsage: rawPeriodicUsage }: Props) {
<FormCard title="subscription.current_plan" description="subscription.current_plan_description">
<div className={styles.planInfo}>
<div className={styles.name}>
<SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />
<SkuName skuId={planId} />
</div>
<div className={styles.description}>
<PlanDescription skuId={currentSkuId} planId={planId} />
<PlanDescription skuId={planId} isEnterprisePlan={isEnterprisePlan} />
</div>
</div>
<FormField title="subscription.plan_usage">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ function SwitchPlanActionBar({ onSubscriptionUpdated, currentSkuId, logtoSkus }:

// Let user contact us when they are currently on Enterprise plan. Do not allow users to self-serve downgrade.
return isEnterprisePlan ? (
<div>
<div key={skuId}>
<a href={contactEmailLink} className={styles.buttonLink} rel="noopener">
<Button title="general.contact_us_action" />
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ function CreateForm({ onClose }: Props) {
<Trans
components={{
a: <ContactUsPhraseLink />,
planName: <SkuName skuId={planId} isEnterprisePlan={isEnterprisePlan} />,
planName: <SkuName skuId={planId} />,
}}
>
{t('upsell.paywall.hooks', {
Expand Down
12 changes: 0 additions & 12 deletions packages/console/src/types/subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,7 @@ import { type InvoicesResponse } from '@/cloud/types/router';

export enum ReservedPlanName {
Free = 'Free',
/** @deprecated */
Hobby = 'Hobby',
Pro = 'Pro',
Enterprise = 'Enterprise',
}

// TODO: use `ReservedPlanId` in the future.
export enum ReservedSkuId {
Free = 'free',
Pro = 'pro',
Development = 'dev',
Admin = 'admin',
Enterprise = 'enterprise',
}

export const localCheckoutSessionGuard = z.object({
Expand Down
17 changes: 0 additions & 17 deletions packages/schemas/src/consts/subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,7 @@
*/
export enum ReservedPlanId {
Free = 'free',
/**
* @deprecated
* In recent refactoring, the `hobby` plan is now treated as the `pro` plan.
* Only use this plan ID to check if a plan is a `pro` plan or not.
* This plan ID will be renamed to `pro` after legacy Stripe data is migrated by @darcyYe
*
* Todo @darcyYe:
* - LOG-7846: Rename `hobby` to `pro` and `pro` to `legacy-pro`
* - LOG-8339: Migrate legacy Stripe data
*/
Hobby = 'hobby',
Pro = 'pro',
Enterprise = 'enterprise',
/**
* @deprecated
* Should not use this plan ID, we only use this tag as a record for the legacy `pro` plan since we will rename the `hobby` plan to be `pro`.
*/
GrandfatheredPro = 'grandfathered-pro',
Development = 'dev',
/**
* This plan ID is reserved for Admin tenant.
Expand Down

0 comments on commit 4b5db6e

Please sign in to comment.