mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(console): add token usage notification banner (#6898)
* feat(console): add token usage notification banner add token usage notificaiton banner * style(console): fix usage error style fix usage error style
This commit is contained in:
parent
6dae2bf4f6
commit
74c9282495
5 changed files with 126 additions and 2 deletions
|
@ -24,7 +24,7 @@
|
|||
color: var(--color-text);
|
||||
|
||||
&.quotaExceeded {
|
||||
color: var(--color-danger-default);
|
||||
color: var(--color-error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -159,7 +159,10 @@ function PlanUsageCard({
|
|||
<div
|
||||
className={classNames(
|
||||
styles.description,
|
||||
typeof usagePercent === 'number' && usagePercent >= 1 && styles.quotaExceeded
|
||||
typeof usagePercent === 'number' &&
|
||||
usagePercent >= 1 &&
|
||||
!isPaidTenant &&
|
||||
styles.quotaExceeded
|
||||
)}
|
||||
>
|
||||
<Trans
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
import { useContext, useMemo, useState } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import { toastResponseError } from '@/cloud/hooks/use-cloud-api';
|
||||
import { type NewSubscriptionPeriodicUsage } from '@/cloud/types/router';
|
||||
import SkuName from '@/components/SkuName';
|
||||
import { subscriptionPage } from '@/consts/pages';
|
||||
import { latestProPlanId } from '@/consts/subscriptions';
|
||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||
import InlineNotification from '@/ds-components/InlineNotification';
|
||||
import { useConfirmModal } from '@/hooks/use-confirm-modal';
|
||||
import useSubscribe from '@/hooks/use-subscribe';
|
||||
import { NotEligibleSwitchSkuModalContent } from '@/pages/TenantSettings/components/NotEligibleSwitchPlanModalContent';
|
||||
import { isPaidPlan, parseExceededSkuQuotaLimitError } from '@/utils/subscription';
|
||||
|
||||
type Props = {
|
||||
readonly className?: string;
|
||||
readonly periodicUsage: NewSubscriptionPeriodicUsage;
|
||||
};
|
||||
|
||||
function TokenLimitExceededNotification({ periodicUsage, className }: Props) {
|
||||
const { currentTenantId } = useContext(TenantsContext);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const {
|
||||
logtoSkus,
|
||||
currentSubscriptionQuota,
|
||||
currentSubscription: { planId, isEnterprisePlan },
|
||||
} = useContext(SubscriptionDataContext);
|
||||
|
||||
const { subscribe } = useSubscribe();
|
||||
const { show } = useConfirmModal();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const proSku = useMemo(() => 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 (
|
||||
<InlineNotification
|
||||
severity={isExceeded ? 'error' : 'alert'}
|
||||
action="subscription.upgrade_pro"
|
||||
className={className}
|
||||
isActionLoading={isLoading}
|
||||
onClick={async () => {
|
||||
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: () => (
|
||||
<NotEligibleSwitchSkuModalContent
|
||||
targetSku={proSku}
|
||||
exceededSkuQuotaKeys={exceededSkuQuotaKeys}
|
||||
/>
|
||||
),
|
||||
title: 'subscription.not_eligible_modal.upgrade_title',
|
||||
confirmButtonText: 'general.got_it',
|
||||
confirmButtonType: 'primary',
|
||||
isCancelButtonVisible: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
void toastResponseError(error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trans
|
||||
components={{
|
||||
planName: <SkuName skuId={planId} />,
|
||||
}}
|
||||
>
|
||||
{t(`subscription.token_usage_notification.${isExceeded ? 'exceeded' : 'close_to_limit'}`)}
|
||||
</Trans>
|
||||
</InlineNotification>
|
||||
);
|
||||
}
|
||||
|
||||
export default TokenLimitExceededNotification;
|
|
@ -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 && (
|
||||
<AddOnUsageChangesNotification className={styles.notification} />
|
||||
)}
|
||||
<TokenLimitExceededNotification
|
||||
periodicUsage={periodicUsage}
|
||||
className={styles.notification}
|
||||
/>
|
||||
<MauLimitExceedNotification periodicUsage={periodicUsage} className={styles.notification} />
|
||||
<PaymentOverdueNotification className={styles.notification} />
|
||||
</FormCard>
|
||||
|
|
|
@ -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 <planName/> 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 <planName/> 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);
|
||||
|
|
Loading…
Reference in a new issue