diff --git a/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss b/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss
index db0b828e8..def6ece6a 100644
--- a/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss
+++ b/packages/console/src/components/PlanUsage/PlanUsageCard/index.module.scss
@@ -24,7 +24,7 @@
color: var(--color-text);
&.quotaExceeded {
- color: var(--color-danger-default);
+ color: var(--color-error);
}
}
diff --git a/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx b/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx
index 7c38fc9ac..6e5c19f23 100644
--- a/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx
+++ b/packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx
@@ -159,7 +159,10 @@ function PlanUsageCard({
= 1 && styles.quotaExceeded
+ typeof usagePercent === 'number' &&
+ usagePercent >= 1 &&
+ !isPaidTenant &&
+ styles.quotaExceeded
)}
>
logtoSkus.find(({ id }) => id === latestProPlanId), [logtoSkus]);
+ const { tokenLimit } = currentSubscriptionQuota;
+
+ const tokenUsagePercent = useMemo(() => {
+ // Unlimited
+ if (tokenLimit === null) {
+ return 0;
+ }
+
+ return periodicUsage.tokenLimit / tokenLimit;
+ }, [periodicUsage.tokenLimit, tokenLimit]);
+
+ if (
+ tokenUsagePercent < 0.9 || // Usage is less than 90%
+ isPaidPlan(planId, isEnterprisePlan) || // Add-on enabled
+ !proSku // Pro SKU not found
+ ) {
+ return null;
+ }
+
+ const isExceeded = tokenUsagePercent >= 1;
+
+ return (
+ {
+ try {
+ setIsLoading(true);
+ await subscribe({
+ skuId: proSku.id,
+ planId: proSku.id,
+ tenantId: currentTenantId,
+ callbackPage: subscriptionPage,
+ });
+ setIsLoading(false);
+ } catch (error: unknown) {
+ setIsLoading(false);
+
+ const [result, exceededSkuQuotaKeys] = await parseExceededSkuQuotaLimitError(error);
+
+ if (result) {
+ await show({
+ ModalContent: () => (
+
+ ),
+ title: 'subscription.not_eligible_modal.upgrade_title',
+ confirmButtonText: 'general.got_it',
+ confirmButtonType: 'primary',
+ isCancelButtonVisible: false,
+ });
+ return;
+ }
+
+ void toastResponseError(error);
+ }
+ }}
+ >
+ ,
+ }}
+ >
+ {t(`subscription.token_usage_notification.${isExceeded ? 'exceeded' : 'close_to_limit'}`)}
+
+
+ );
+}
+
+export default TokenLimitExceededNotification;
diff --git a/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/index.tsx b/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/index.tsx
index 29066d2f5..f2a01bacf 100644
--- a/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/index.tsx
+++ b/packages/console/src/pages/TenantSettings/Subscription/CurrentPlan/index.tsx
@@ -13,6 +13,7 @@ import { isPaidPlan } from '@/utils/subscription';
import AddOnUsageChangesNotification from './AddOnUsageChangesNotification';
import MauLimitExceedNotification from './MauLimitExceededNotification';
import PaymentOverdueNotification from './PaymentOverdueNotification';
+import TokenLimitExceededNotification from './TokenLimitExceededNotification';
import styles from './index.module.scss';
type Props = {
@@ -58,6 +59,10 @@ function CurrentPlan({ periodicUsage, usageAddOnSkus }: Props) {
{isPaidPlan(planId, isEnterprisePlan) && !isEnterprisePlan && (
)}
+
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 488e033e5..7b0812f8a 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
@@ -69,6 +69,12 @@ const subscription = {
subscription_check_timeout: 'Subscription check timed out. Please refresh later.',
no_subscription: 'No subscription',
usage,
+ token_usage_notification: {
+ exceeded:
+ 'You have exceeded your
token usage limit. Users will not be able to access the Logto service properly. Please upgrade your plan to premium promptly to avoid any inconvenience.',
+ close_to_limit:
+ 'You almost reached your
token usage limit. Logto will stop granting tokens when the limit is reached. Please upgrade your plan to premium to avoid any inconvenience.',
+ },
};
export default Object.freeze(subscription);