diff --git a/packages/console/package.json b/packages/console/package.json
index 6a92be7bd..01a0ce381 100644
--- a/packages/console/package.json
+++ b/packages/console/package.json
@@ -27,7 +27,7 @@
"devDependencies": {
"@fontsource/roboto-mono": "^5.0.0",
"@jest/types": "^29.5.0",
- "@logto/cloud": "0.2.5-582d792",
+ "@logto/cloud": "0.2.5-20fd0a2",
"@logto/connector-kit": "workspace:^4.0.0",
"@logto/core-kit": "workspace:^2.5.0",
"@logto/elements": "workspace:^0.0.0",
diff --git a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx
index 580729698..342dbb6d6 100644
--- a/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx
+++ b/packages/console/src/components/ApplicationCreation/CreateForm/Footer/index.tsx
@@ -13,6 +13,7 @@ import Button from '@/ds-components/Button';
import TextLink from '@/ds-components/TextLink';
import useApplicationsUsage from '@/hooks/use-applications-usage';
import useUserPreferences from '@/hooks/use-user-preferences';
+import { isPaidPlan } from '@/utils/subscription';
import styles from './index.module.scss';
@@ -26,7 +27,7 @@ type Props = {
function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props) {
const {
currentSku,
- currentSubscription: { planId, isAddOnAvailable },
+ currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
currentSubscriptionQuota,
} = useContext(SubscriptionDataContext);
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' });
@@ -45,7 +46,8 @@ function Footer({ selectedType, isLoading, onClickCreate, isThirdParty }: Props)
selectedType === ApplicationType.MachineToMachine &&
isAddOnAvailable &&
hasMachineToMachineAppsReachedLimit &&
- planId === ReservedPlanId.Pro &&
+ // Just in case the enterprise plan has reached the resource limit, we still need to show charge notice.
+ isPaidPlan(planId, isEnterprisePlan) &&
!m2mUpsellNoticeAcknowledged
) {
return (
diff --git a/packages/console/src/components/FeatureTag/index.tsx b/packages/console/src/components/FeatureTag/index.tsx
index 764eea2d1..04814275f 100644
--- a/packages/console/src/components/FeatureTag/index.tsx
+++ b/packages/console/src/components/FeatureTag/index.tsx
@@ -80,9 +80,14 @@ type CombinedAddOnAndFeatureTagProps = {
export function CombinedAddOnAndFeatureTag(props: CombinedAddOnAndFeatureTagProps) {
const { hasAddOnTag, className, paywall } = props;
const {
- currentSubscription: { planId, isAddOnAvailable },
+ currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
} = useContext(SubscriptionDataContext);
+ // We believe that the enterprise plan has already allocated sufficient resource quotas in the deal negotiation, so there is no need for upselling, nor will it trigger the add-on tag prompt.
+ if (isEnterprisePlan) {
+ return null;
+ }
+
// Show the "Add-on" tag for Pro plan when dev features enabled.
if (hasAddOnTag && isAddOnAvailable && isCloud && planId === ReservedPlanId.Pro) {
return (
diff --git a/packages/console/src/components/PlanDescription/index.tsx b/packages/console/src/components/PlanDescription/index.tsx
index 1db3e0785..96d35edac 100644
--- a/packages/console/src/components/PlanDescription/index.tsx
+++ b/packages/console/src/components/PlanDescription/index.tsx
@@ -10,6 +10,7 @@ const registeredPlanDescriptionPhrasesMap: Record<
> = {
[ReservedPlanId.Free]: 'free_plan_description',
[ReservedPlanId.Pro]: 'pro_plan_description',
+ [ReservedPlanId.Enterprise]: 'enterprise_description',
};
type Props = {
diff --git a/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss b/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss
index 00bfcdd64..d3e078369 100644
--- a/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss
+++ b/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss
@@ -31,6 +31,10 @@
.usageTip {
font: var(--font-body-2);
color: var(--color-text-secondary);
+
+ &.hidden {
+ display: none;
+ }
}
.tag {
diff --git a/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx b/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx
index d44bdeaba..1af08a91b 100644
--- a/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx
+++ b/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx
@@ -22,6 +22,7 @@ export type Props = {
readonly titleKey: AdminConsoleKey;
readonly tooltipKey?: AdminConsoleKey;
readonly unitPrice: number;
+ readonly isUsageTipHidden: boolean;
readonly className?: string;
};
@@ -32,6 +33,7 @@ function PlanUsageCard({
usageKey,
titleKey,
tooltipKey,
+ isUsageTipHidden,
className,
}: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
@@ -75,7 +77,9 @@ function PlanUsageCard({
>
,
+ span: (
+
+ ),
}}
>
{t(usageKey, {
diff --git a/packages/console/src/components/PlanUsage/index.tsx b/packages/console/src/components/PlanUsage/index.tsx
index 946f1b6dc..6dc5e5b34 100644
--- a/packages/console/src/components/PlanUsage/index.tsx
+++ b/packages/console/src/components/PlanUsage/index.tsx
@@ -22,13 +22,16 @@ function PlanUsage({ periodicUsage: rawPeriodicUsage }: Props) {
const {
currentSubscriptionQuota,
currentSubscriptionUsage,
- currentSubscription: currentSubscriptionFromNewPricingModel,
+ currentSubscription: {
+ currentPeriodStart,
+ currentPeriodEnd,
+ planId,
+ isAddOnAvailable,
+ isEnterprisePlan,
+ },
} = useContext(SubscriptionDataContext);
const { currentTenant } = useContext(TenantsContext);
- const { currentPeriodStart, currentPeriodEnd, planId, isAddOnAvailable } =
- currentSubscriptionFromNewPricingModel;
-
const periodicUsage = useMemo(
() =>
rawPeriodicUsage ??
@@ -71,6 +74,8 @@ function PlanUsage({ periodicUsage: rawPeriodicUsage }: Props) {
...cond(
(key === 'tokenLimit' || key === 'mauLimit') && { quota: currentSubscriptionQuota[key] }
),
+ // Hide usage tip for Enterprise plan.
+ isUsageTipHidden: isEnterprisePlan,
}));
return (
@@ -83,7 +88,7 @@ function PlanUsage({ periodicUsage: rawPeriodicUsage }: Props) {
periodStart: currentPeriodStart,
periodEnd: currentPeriodEnd,
}),
- renewDate: dayjs(currentPeriodEnd).add(1, 'day').format('MMM D, YYYY'),
+ renewDate: dayjs(currentPeriodEnd).format('MMM D, YYYY'),
}}
/>
diff --git a/packages/console/src/components/Topbar/TenantSelector/TenantDropdownItem/index.tsx b/packages/console/src/components/Topbar/TenantSelector/TenantDropdownItem/index.tsx
index c697bec64..947de5b6c 100644
--- a/packages/console/src/components/Topbar/TenantSelector/TenantDropdownItem/index.tsx
+++ b/packages/console/src/components/Topbar/TenantSelector/TenantDropdownItem/index.tsx
@@ -1,4 +1,4 @@
-import { TenantTag } from '@logto/schemas';
+import { TenantTag, ReservedPlanId } from '@logto/schemas';
import classNames from 'classnames';
import { useContext, useMemo } from 'react';
@@ -23,7 +23,7 @@ function TenantDropdownItem({ tenantData, isSelected, onClick }: Props) {
const {
name,
tag,
- subscription: { planId },
+ subscription: { planId, isEnterprisePlan },
} = tenantData;
const { logtoSkus } = useContext(SubscriptionDataContext);
@@ -31,6 +31,7 @@ function TenantDropdownItem({ tenantData, isSelected, onClick }: Props) {
() => logtoSkus.find(({ id }) => id === planId),
[logtoSkus, planId]
);
+ const skuId = isEnterprisePlan ? ReservedPlanId.Enterprise : planId;
if (!tenantSubscriptionSku) {
return null;
@@ -48,7 +49,7 @@ function TenantDropdownItem({ tenantData, isSelected, onClick }: Props) {
{tag === TenantTag.Development ? (
) : (
-
+
)}
diff --git a/packages/console/src/consts/tenants.ts b/packages/console/src/consts/tenants.ts
index 3ef691431..5024bb2c4 100644
--- a/packages/console/src/consts/tenants.ts
+++ b/packages/console/src/consts/tenants.ts
@@ -31,6 +31,7 @@ export const defaultTenantResponse: TenantResponse = {
planId: defaultSubscriptionPlanId,
currentPeriodStart: dayjs().toDate(),
currentPeriodEnd: dayjs().add(1, 'month').toDate(),
+ isEnterprisePlan: false,
},
usage: {
activeUsers: 0,
diff --git a/packages/console/src/pages/ApiResources/components/CreateForm/Footer.tsx b/packages/console/src/pages/ApiResources/components/CreateForm/Footer.tsx
index 6e927b9cd..2b7906280 100644
--- a/packages/console/src/pages/ApiResources/components/CreateForm/Footer.tsx
+++ b/packages/console/src/pages/ApiResources/components/CreateForm/Footer.tsx
@@ -13,6 +13,7 @@ import Button from '@/ds-components/Button';
import TextLink from '@/ds-components/TextLink';
import useApiResourcesUsage from '@/hooks/use-api-resources-usage';
import useUserPreferences from '@/hooks/use-user-preferences';
+import { isPaidPlan } from '@/utils/subscription';
import styles from './index.module.scss';
@@ -24,7 +25,7 @@ type Props = {
function Footer({ isCreationLoading, onClickCreate }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
- currentSubscription: { planId, isAddOnAvailable },
+ currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
currentSubscriptionUsage: { resourcesLimit },
currentSku,
} = useContext(SubscriptionDataContext);
@@ -60,7 +61,8 @@ function Footer({ isCreationLoading, onClickCreate }: Props) {
if (
isAddOnAvailable &&
hasReachedLimit &&
- planId === ReservedPlanId.Pro &&
+ // Just in case the enterprise plan has reached the resource limit, we still need to show charge notice.
+ isPaidPlan(planId, isEnterprisePlan) &&
!apiResourceUpsellNoticeAcknowledged
) {
return (
diff --git a/packages/console/src/pages/EnterpriseSso/SsoCreationModal/index.tsx b/packages/console/src/pages/EnterpriseSso/SsoCreationModal/index.tsx
index dc78727a6..f22f8f6cc 100644
--- a/packages/console/src/pages/EnterpriseSso/SsoCreationModal/index.tsx
+++ b/packages/console/src/pages/EnterpriseSso/SsoCreationModal/index.tsx
@@ -31,6 +31,7 @@ import useApi, { type RequestError } from '@/hooks/use-api';
import useUserPreferences from '@/hooks/use-user-preferences';
import modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';
+import { isPaidPlan } from '@/utils/subscription';
import SsoConnectorRadioGroup from './SsoConnectorRadioGroup';
import styles from './index.module.scss';
@@ -50,7 +51,7 @@ const duplicateConnectorNameErrorCode = 'single_sign_on.duplicate_connector_name
function SsoCreationModal({ isOpen, onClose: rawOnClose }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
- currentSubscription: { planId, isAddOnAvailable },
+ currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
currentSubscriptionQuota,
} = useContext(SubscriptionDataContext);
const {
@@ -160,7 +161,8 @@ function SsoCreationModal({ isOpen, onClose: rawOnClose }: Props) {
footer={
conditional(
isAddOnAvailable &&
- planId === ReservedPlanId.Pro &&
+ // Just in case the enterprise plan has reached the resource limit, we still need to show charge notice.
+ isPaidPlan(planId, isEnterprisePlan) &&
!enterpriseSsoUpsellNoticeAcknowledged && (
{t('upsell.add_on.footer.enterprise_sso', {
price: enterpriseSsoAddOnUnitPrice,
- planName: t('subscription.pro_plan'),
+ planName: t(
+ isEnterprisePlan ? 'subscription.enterprise' : 'subscription.pro_plan'
+ ),
})}
diff --git a/packages/console/src/pages/Organizations/CreateOrganizationModal/index.tsx b/packages/console/src/pages/Organizations/CreateOrganizationModal/index.tsx
index 8cedab786..e1b5a23c5 100644
--- a/packages/console/src/pages/Organizations/CreateOrganizationModal/index.tsx
+++ b/packages/console/src/pages/Organizations/CreateOrganizationModal/index.tsx
@@ -21,6 +21,7 @@ import useApi from '@/hooks/use-api';
import useUserPreferences from '@/hooks/use-user-preferences';
import modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';
+import { isPaidPlan } from '@/utils/subscription';
import styles from './index.module.scss';
@@ -33,7 +34,7 @@ function CreateOrganizationModal({ isOpen, onClose }: Props) {
const api = useApi();
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const {
- currentSubscription: { planId, isAddOnAvailable },
+ currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
currentSubscriptionQuota,
} = useContext(SubscriptionDataContext);
const {
@@ -85,7 +86,8 @@ function CreateOrganizationModal({ isOpen, onClose }: Props) {
footer={
cond(
isAddOnAvailable &&
- planId === ReservedPlanId.Pro &&
+ // Just in case the enterprise plan has reached the resource limit, we still need to show charge notice.
+ isPaidPlan(planId, isEnterprisePlan) &&
!organizationUpsellNoticeAcknowledged && (
{t('upsell.add_on.footer.organization', {
price: organizationAddOnUnitPrice,
- planName: t('subscription.pro_plan'),
+ planName: t(
+ isEnterprisePlan ? 'subscription.enterprise' : 'subscription.pro_plan'
+ ),
})}
diff --git a/packages/console/src/pages/TenantSettings/BillingHistory/index.tsx b/packages/console/src/pages/TenantSettings/BillingHistory/index.tsx
index 416f6d861..4bde6724d 100644
--- a/packages/console/src/pages/TenantSettings/BillingHistory/index.tsx
+++ b/packages/console/src/pages/TenantSettings/BillingHistory/index.tsx
@@ -1,3 +1,4 @@
+import { ReservedPlanId } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import dayjs from 'dayjs';
import { useCallback, useContext, useMemo } from 'react';
@@ -50,7 +51,19 @@ function BillingHistory() {
{
title: ,
dataIndex: 'planName',
- render: ({ skuId, periodStart, periodEnd }) => {
+ 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);
+
return (
currentSubscription.upcomingInvoice?.subtotal ?? currentSku.unitPrice ?? 0,
- [currentSku.unitPrice, currentSubscription.upcomingInvoice]
+ () => upcomingInvoice?.subtotal ?? unitPrice ?? 0,
+ [unitPrice, upcomingInvoice]
);
if (!periodicUsage) {
@@ -53,10 +59,10 @@ function CurrentPlan({ periodicUsage: rawPeriodicUsage }: Props) {
@@ -65,7 +71,7 @@ function CurrentPlan({ periodicUsage: rawPeriodicUsage }: Props) {
- {currentSubscription.isAddOnAvailable && (
+ {isAddOnAvailable && !isEnterprisePlan && (
)}
();
@@ -118,7 +122,14 @@ function SwitchPlanActionBar({ onSubscriptionUpdated, currentSkuId, logtoSkus }:
const isCurrentSku = currentSkuId === skuId;
const isDowngrade = isDowngradePlan(currentSkuId, skuId);
- return (
+ // Let user contact us for Pro plan when they are currently on Enterprise plan.
+ return isEnterprisePlan && skuId === ReservedPlanId.Pro ? (
+
+ ) : (
diff --git a/packages/console/src/pages/TenantSettings/TenantMembers/InviteMemberModal/index.tsx b/packages/console/src/pages/TenantSettings/TenantMembers/InviteMemberModal/index.tsx
index d4f07a230..c69d59c17 100644
--- a/packages/console/src/pages/TenantSettings/TenantMembers/InviteMemberModal/index.tsx
+++ b/packages/console/src/pages/TenantSettings/TenantMembers/InviteMemberModal/index.tsx
@@ -20,6 +20,7 @@ import { useConfirmModal } from '@/hooks/use-confirm-modal';
import useUserPreferences from '@/hooks/use-user-preferences';
import modalStyles from '@/scss/modal.module.scss';
import { hasReachedSubscriptionQuotaLimit } from '@/utils/quota';
+import { isPaidPlan } from '@/utils/subscription';
import InviteEmailsInput from '../InviteEmailsInput';
import useEmailInputUtils from '../InviteEmailsInput/hooks';
@@ -42,7 +43,7 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
const { parseEmailOptions } = useEmailInputUtils();
const { show } = useConfirmModal();
const {
- currentSubscription: { planId, isAddOnAvailable },
+ currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
currentSubscriptionQuota,
currentSubscriptionUsage: { tenantMembersLimit },
mutateSubscriptionQuotaAndUsages,
@@ -138,7 +139,8 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
conditional(
isAddOnAvailable &&
hasTenantMembersReachedLimit &&
- planId === ReservedPlanId.Pro &&
+ // Just in case the enterprise plan has reached the resource limit, we still need to show charge notice.
+ isPaidPlan(planId, isEnterprisePlan) &&
!tenantMembersUpsellNoticeAcknowledged && (
logtoSkus.filter(({ id }) => featuredPlanIds.includes(id));
+
+export const isPaidPlan = (planId: string, isEnterprisePlan: boolean) =>
+ planId === ReservedPlanId.Pro || isEnterprisePlan;
diff --git a/packages/core/package.json b/packages/core/package.json
index 8abf83039..511c484db 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -97,7 +97,7 @@
"zod": "^3.23.8"
},
"devDependencies": {
- "@logto/cloud": "0.2.5-582d792",
+ "@logto/cloud": "0.2.5-20fd0a2",
"@silverhand/eslint-config": "6.0.1",
"@silverhand/ts-config": "6.0.0",
"@types/adm-zip": "^0.5.5",
diff --git a/packages/core/src/libraries/quota.ts b/packages/core/src/libraries/quota.ts
index 3c4716d06..1209a3709 100644
--- a/packages/core/src/libraries/quota.ts
+++ b/packages/core/src/libraries/quota.ts
@@ -17,11 +17,15 @@ export type QuotaLibrary = ReturnType;
/**
* @remarks
* Should report usage changes to the Cloud only when the following conditions are met:
- * 1. The tenant is on the Pro plan.
+ * 1. The tenant is either on Pro plan or Enterprise plan.
* 2. The usage key is add-on related usage key.
*/
-const shouldReportSubscriptionUpdates = (planId: string, key: keyof SubscriptionQuota) =>
- planId === ReservedPlanId.Pro && isReportSubscriptionUpdatesUsageKey(key);
+const shouldReportSubscriptionUpdates = (
+ planId: string,
+ isEnterprisePlan: boolean,
+ key: keyof SubscriptionQuota
+) =>
+ (planId === ReservedPlanId.Pro || isEnterprisePlan) && isReportSubscriptionUpdatesUsageKey(key);
export const createQuotaLibrary = (cloudConnection: CloudConnectionLibrary) => {
const guardTenantUsageByKey = async (key: keyof SubscriptionUsage) => {
@@ -41,10 +45,11 @@ export const createQuotaLibrary = (cloudConnection: CloudConnectionLibrary) => {
planId,
quota: fullQuota,
usage: fullUsage,
+ isEnterprisePlan,
} = await getTenantSubscriptionData(cloudConnection);
- // Do not block Pro plan from adding add-on resources.
- if (shouldReportSubscriptionUpdates(planId, key)) {
+ // Do not block Pro/Enterprise plan from adding add-on resources.
+ if (shouldReportSubscriptionUpdates(planId, isEnterprisePlan, key)) {
return;
}
@@ -159,9 +164,11 @@ export const createQuotaLibrary = (cloudConnection: CloudConnectionLibrary) => {
return;
}
- const { planId, isAddOnAvailable } = await getTenantSubscriptionData(cloudConnection);
+ const { planId, isAddOnAvailable, isEnterprisePlan } = await getTenantSubscriptionData(
+ cloudConnection
+ );
- if (shouldReportSubscriptionUpdates(planId, key) && isAddOnAvailable) {
+ if (shouldReportSubscriptionUpdates(planId, isEnterprisePlan, key) && isAddOnAvailable) {
await reportSubscriptionUpdates(cloudConnection, key);
}
};
diff --git a/packages/core/src/utils/subscription/index.ts b/packages/core/src/utils/subscription/index.ts
index 34f5416ff..d53a59520 100644
--- a/packages/core/src/utils/subscription/index.ts
+++ b/packages/core/src/utils/subscription/index.ts
@@ -23,6 +23,7 @@ export const getTenantSubscriptionData = async (
cloudConnection: CloudConnectionLibrary
): Promise<{
planId: string;
+ isEnterprisePlan: boolean;
isAddOnAvailable?: boolean;
quota: SubscriptionQuota;
usage: SubscriptionUsage;
@@ -30,12 +31,13 @@ export const getTenantSubscriptionData = async (
roles: Record;
}> => {
const client = await cloudConnection.getClient();
- const [{ planId, isAddOnAvailable }, { quota, usage, resources, roles }] = await Promise.all([
- client.get('/api/tenants/my/subscription'),
- client.get('/api/tenants/my/subscription-usage'),
- ]);
+ const [{ planId, isAddOnAvailable, isEnterprisePlan }, { quota, usage, resources, roles }] =
+ await Promise.all([
+ client.get('/api/tenants/my/subscription'),
+ client.get('/api/tenants/my/subscription-usage'),
+ ]);
- return { planId, isAddOnAvailable, quota, usage, resources, roles };
+ return { planId, isEnterprisePlan, isAddOnAvailable, quota, usage, resources, roles };
};
export const reportSubscriptionUpdates = async (
diff --git a/packages/phrases/src/locales/ar/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/ar/translation/admin-console/subscription/index.ts
index 70a79f365..08943b5d0 100644
--- a/packages/phrases/src/locales/ar/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/ar/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan: 'خطة Pro',
pro_plan_description: 'للاستفادة من الأعمال بدون قلق مع Logto.',
enterprise: 'الشركات',
+ /** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
current_plan: 'الخطة الحالية',
current_plan_description:
'هذه هي الخطة الحالية الخاصة بك. يمكنك بسهولة رؤية استخدام الخطة الخاصة بك ، والتحقق من فاتورتك القادمة ، وإجراء التغييرات على الخطة حسب الحاجة.',
diff --git a/packages/phrases/src/locales/de/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/de/translation/admin-console/subscription/index.ts
index 5050e91a0..2a624a788 100644
--- a/packages/phrases/src/locales/de/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/de/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan_description: 'Für Unternehmen, die sorgenfrei von Logto profitieren möchten.',
enterprise: 'Unternehmen',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/en/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/en/translation/admin-console/subscription/index.ts
index 84d95b940..5f49e721e 100644
--- a/packages/phrases/src/locales/en/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/en/translation/admin-console/subscription/index.ts
@@ -8,6 +8,8 @@ const subscription = {
pro_plan: 'Pro plan',
pro_plan_description: 'For businesses benefit worry-free with Logto.',
enterprise: 'Enterprise',
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
admin_plan: 'Admin plan',
dev_plan: 'Development plan',
current_plan: 'Current plan',
diff --git a/packages/phrases/src/locales/es/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/es/translation/admin-console/subscription/index.ts
index 987be9e7b..e771bbc76 100644
--- a/packages/phrases/src/locales/es/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/es/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'Benefíciese sin preocupaciones con Logto para empresas.',
enterprise: 'Empresa',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/fr/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/fr/translation/admin-console/subscription/index.ts
index 79338d7a0..36c8f5269 100644
--- a/packages/phrases/src/locales/fr/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/fr/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'Pour les entreprises qui bénéficient de Logto sans soucis.',
enterprise: 'Entreprise',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/it/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/it/translation/admin-console/subscription/index.ts
index 6d9116bad..1d9f06d9a 100644
--- a/packages/phrases/src/locales/it/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/it/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'Per aziende che beneficiano di Logto senza preoccupazioni.',
enterprise: 'Azienda',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/ja/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/ja/translation/admin-console/subscription/index.ts
index 7821ef9f3..2e3b3a785 100644
--- a/packages/phrases/src/locales/ja/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/ja/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'ビジネスが安心してLogtoを利用できるプランです。',
enterprise: 'エンタープライズ',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/ko/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/ko/translation/admin-console/subscription/index.ts
index 2313f18e3..3aa271ba7 100644
--- a/packages/phrases/src/locales/ko/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/ko/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan_description: 'Logto와 함께 걱정 없이 비즈니스 혜택을 받으세요.',
enterprise: '기업용',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/pl-pl/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/pl-pl/translation/admin-console/subscription/index.ts
index 00fea6ebe..d1a53a618 100644
--- a/packages/phrases/src/locales/pl-pl/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/pl-pl/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'Dla firm, ciesz się bezstresową obsługą Logto.',
enterprise: 'Przedsiębiorstwo',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/pt-br/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/pt-br/translation/admin-console/subscription/index.ts
index 9ace00cbf..75009d105 100644
--- a/packages/phrases/src/locales/pt-br/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/pt-br/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'Para empresas se beneficiarem tranquilo com o Logto.',
enterprise: 'Empresa',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/pt-pt/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/pt-pt/translation/admin-console/subscription/index.ts
index b62f1ca38..1064da065 100644
--- a/packages/phrases/src/locales/pt-pt/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/pt-pt/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: 'Para empresas que desejam se beneficiar sem preocupações com o Logto.',
enterprise: 'Empresa',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/ru/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/ru/translation/admin-console/subscription/index.ts
index 8120b0a7c..b0ecd6d85 100644
--- a/packages/phrases/src/locales/ru/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/ru/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan_description: 'Позволяет бизнесу использовать Logto без забот.',
enterprise: 'Enterprise',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/tr-tr/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/tr-tr/translation/admin-console/subscription/index.ts
index 4444a6791..82928ae94 100644
--- a/packages/phrases/src/locales/tr-tr/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/tr-tr/translation/admin-console/subscription/index.ts
@@ -9,6 +9,9 @@ const subscription = {
pro_plan_description: "Endişesiz bir şekilde Logto'dan faydalanan işletmeler için.",
enterprise: 'Kurumsal',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/zh-cn/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/zh-cn/translation/admin-console/subscription/index.ts
index a51a37723..2599292f6 100644
--- a/packages/phrases/src/locales/zh-cn/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/zh-cn/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan_description: '适用于企业付费无忧。',
enterprise: '企业',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/zh-hk/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/zh-hk/translation/admin-console/subscription/index.ts
index 6a59c5e9b..eaab53f32 100644
--- a/packages/phrases/src/locales/zh-hk/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/zh-hk/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan_description: '供企業放心使用Logto。',
enterprise: '企業',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/phrases/src/locales/zh-tw/translation/admin-console/subscription/index.ts b/packages/phrases/src/locales/zh-tw/translation/admin-console/subscription/index.ts
index d912ea3aa..a2ffa3063 100644
--- a/packages/phrases/src/locales/zh-tw/translation/admin-console/subscription/index.ts
+++ b/packages/phrases/src/locales/zh-tw/translation/admin-console/subscription/index.ts
@@ -8,6 +8,9 @@ const subscription = {
pro_plan_description: '企業無憂享受 Logto 服務。',
enterprise: '企業版',
/** UNTRANSLATED */
+ enterprise_description:
+ 'For large-scale organizations requiring advanced features, full customization, and dedicated support to power mission-critical applications. Tailored to your needs for ultimate security, compliance, and performance.',
+ /** UNTRANSLATED */
admin_plan: 'Admin plan',
/** UNTRANSLATED */
dev_plan: 'Development plan',
diff --git a/packages/schemas/src/consts/subscriptions.ts b/packages/schemas/src/consts/subscriptions.ts
index 0161b7c94..5071b2fe5 100644
--- a/packages/schemas/src/consts/subscriptions.ts
+++ b/packages/schemas/src/consts/subscriptions.ts
@@ -18,6 +18,7 @@ export enum ReservedPlanId {
*/
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`.
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 782a9d5d9..a189d24d1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2568,8 +2568,8 @@ importers:
specifier: ^29.5.0
version: 29.5.0
'@logto/cloud':
- specifier: 0.2.5-582d792
- version: 0.2.5-582d792(zod@3.23.8)
+ specifier: 0.2.5-20fd0a2
+ version: 0.2.5-20fd0a2(zod@3.23.8)
'@logto/connector-kit':
specifier: workspace:^4.0.0
version: link:../toolkit/connector-kit
@@ -3064,8 +3064,8 @@ importers:
version: 3.23.8
devDependencies:
'@logto/cloud':
- specifier: 0.2.5-582d792
- version: 0.2.5-582d792(zod@3.23.8)
+ specifier: 0.2.5-20fd0a2
+ version: 0.2.5-20fd0a2(zod@3.23.8)
'@silverhand/eslint-config':
specifier: 6.0.1
version: 6.0.1(eslint@8.57.0)(prettier@3.0.0)(typescript@5.5.3)
@@ -5571,6 +5571,10 @@ packages:
'@logto/client@2.7.2':
resolution: {integrity: sha512-jsmuDl9QpXfR3uLEMPE67tvYoL5XcjJi+4yGqucYPjd4GH6SUHp3N9skk8C/OyygnKDPLY+ttwD0LaIbpGvn+Q==}
+ '@logto/cloud@0.2.5-20fd0a2':
+ resolution: {integrity: sha512-j0f2RDpi/OEI59WXKnih7QeFSywNFV91PkulZdmcGa8HCRNmht94siw+LILzheg6bzwfvHU/aN4tJYL1/Px1BA==}
+ engines: {node: ^20.9.0}
+
'@logto/cloud@0.2.5-582d792':
resolution: {integrity: sha512-0fIZzqwyjQguTS0a5+XbgVZlGEB/MXIf6pbuBDkHh6JHlMTJ/XH041rWX+e+nMk5N7/Xk2XXS+d2RJUWumnmpw==}
engines: {node: ^20.9.0}
@@ -15240,6 +15244,13 @@ snapshots:
camelcase-keys: 7.0.2
jose: 5.6.3
+ '@logto/cloud@0.2.5-20fd0a2(zod@3.23.8)':
+ dependencies:
+ '@silverhand/essentials': 2.9.1
+ '@withtyped/server': 0.14.0(zod@3.23.8)
+ transitivePeerDependencies:
+ - zod
+
'@logto/cloud@0.2.5-582d792(zod@3.23.8)':
dependencies:
'@silverhand/essentials': 2.9.1