0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor: stop using GET /:tenantId/usage API and GET /tenants usage field (#6434)

This commit is contained in:
Darcy Ye 2024-08-12 19:47:08 +08:00 committed by GitHub
parent b42b37bb72
commit c791847879
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 23 additions and 98 deletions

View file

@ -15,9 +15,6 @@ export type LogtoSkuResponse = GetArrayElementType<GuardedResponse<GetRoutes['/a
export type Subscription = GuardedResponse<GetRoutes['/api/tenants/:tenantId/subscription']>; export type Subscription = GuardedResponse<GetRoutes['/api/tenants/:tenantId/subscription']>;
/** @deprecated */
export type SubscriptionUsage = GuardedResponse<GetRoutes['/api/tenants/:tenantId/usage']>;
/* ===== Use `New` in the naming to avoid confusion with legacy types ===== */ /* ===== Use `New` in the naming to avoid confusion with legacy types ===== */
export type NewSubscriptionUsageResponse = GuardedResponse< export type NewSubscriptionUsageResponse = GuardedResponse<
GetRoutes['/api/tenants/:tenantId/subscription-usage'] GetRoutes['/api/tenants/:tenantId/subscription-usage']

View file

@ -4,10 +4,8 @@ import ReactModal from 'react-modal';
import PlanUsage from '@/components/PlanUsage'; import PlanUsage from '@/components/PlanUsage';
import { contactEmailLink } from '@/consts'; import { contactEmailLink } from '@/consts';
import { isDevFeaturesEnabled } from '@/consts/env';
import { subscriptionPage } from '@/consts/pages'; import { subscriptionPage } from '@/consts/pages';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import Button from '@/ds-components/Button'; import Button from '@/ds-components/Button';
import FormField from '@/ds-components/FormField'; import FormField from '@/ds-components/FormField';
import InlineNotification from '@/ds-components/InlineNotification'; import InlineNotification from '@/ds-components/InlineNotification';
@ -20,8 +18,6 @@ import PlanName from '../PlanName';
import styles from './index.module.scss'; import styles from './index.module.scss';
function MauExceededModal() { function MauExceededModal() {
const { currentTenant } = useContext(TenantsContext);
const { usage } = currentTenant ?? {};
const { const {
currentPlan, currentPlan,
currentSubscription, currentSubscription,
@ -38,19 +34,15 @@ function MauExceededModal() {
setHasClosed(true); setHasClosed(true);
}; };
if (!usage || hasClosed) { if (hasClosed) {
return null; return null;
} }
const { const { name: planName } = currentPlan;
quota: { mauLimit },
name: planName,
} = currentPlan;
const isMauExceeded = isDevFeaturesEnabled const isMauExceeded =
? currentSubscriptionQuota.mauLimit !== null && currentSubscriptionQuota.mauLimit !== null &&
currentSubscriptionUsage.mauLimit >= currentSubscriptionQuota.mauLimit currentSubscriptionUsage.mauLimit >= currentSubscriptionQuota.mauLimit;
: mauLimit !== null && usage.activeUsers >= mauLimit;
if (!isMauExceeded) { if (!isMauExceeded) {
return null; return null;
@ -93,11 +85,7 @@ function MauExceededModal() {
</Trans> </Trans>
</InlineNotification> </InlineNotification>
<FormField title="subscription.plan_usage"> <FormField title="subscription.plan_usage">
<PlanUsage <PlanUsage currentSubscription={currentSubscription} currentPlan={currentPlan} />
subscriptionUsage={usage}
currentSubscription={currentSubscription}
currentPlan={currentPlan}
/>
</FormField> </FormField>
</ModalLayout> </ModalLayout>
</ReactModal> </ReactModal>

View file

@ -4,7 +4,7 @@ import classNames from 'classnames';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useContext } from 'react'; import { useContext } from 'react';
import { type SubscriptionUsage, type Subscription } from '@/cloud/types/router'; import { type Subscription } from '@/cloud/types/router';
import { isDevFeaturesEnabled } from '@/consts/env'; import { isDevFeaturesEnabled } from '@/consts/env';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import DynamicT from '@/ds-components/DynamicT'; import DynamicT from '@/ds-components/DynamicT';
@ -16,15 +16,13 @@ import styles from './index.module.scss';
import { usageKeys, usageKeyMap, titleKeyMap, tooltipKeyMap } from './utils'; import { usageKeys, usageKeyMap, titleKeyMap, tooltipKeyMap } from './utils';
type Props = { type Props = {
/** @deprecated */
readonly subscriptionUsage: SubscriptionUsage;
/** @deprecated */ /** @deprecated */
readonly currentSubscription: Subscription; readonly currentSubscription: Subscription;
/** @deprecated */ /** @deprecated */
readonly currentPlan: SubscriptionPlan; readonly currentPlan: SubscriptionPlan;
}; };
function PlanUsage({ subscriptionUsage, currentSubscription, currentPlan }: Props) { function PlanUsage({ currentSubscription, currentPlan }: Props) {
const { const {
currentSubscriptionQuota, currentSubscriptionQuota,
currentSubscriptionUsage, currentSubscriptionUsage,
@ -35,9 +33,10 @@ function PlanUsage({ subscriptionUsage, currentSubscription, currentPlan }: Prop
? currentSubscriptionFromNewPricingModel ? currentSubscriptionFromNewPricingModel
: currentSubscription; : currentSubscription;
const [activeUsers, mauLimit] = isDevFeaturesEnabled const [activeUsers, mauLimit] = [
? [currentSubscriptionUsage.mauLimit, currentSubscriptionQuota.mauLimit] currentSubscriptionUsage.mauLimit,
: [subscriptionUsage.activeUsers, currentPlan.quota.mauLimit]; isDevFeaturesEnabled ? currentSubscriptionQuota.mauLimit : currentPlan.quota.mauLimit,
];
const usagePercent = conditional(mauLimit && activeUsers / mauLimit); const usagePercent = conditional(mauLimit && activeUsers / mauLimit);

View file

@ -10,7 +10,7 @@ import {
defaultSubscriptionQuota, defaultSubscriptionQuota,
defaultSubscriptionUsage, defaultSubscriptionUsage,
} from '@/consts'; } from '@/consts';
import { isCloud, isDevFeaturesEnabled } from '@/consts/env'; import { isCloud } from '@/consts/env';
import { TenantsContext } from '@/contexts/TenantsProvider'; import { TenantsContext } from '@/contexts/TenantsProvider';
import useLogtoSkus from '@/hooks/use-logto-skus'; import useLogtoSkus from '@/hooks/use-logto-skus';
@ -36,7 +36,7 @@ const useNewSubscriptionData: () => NewSubscriptionContext & { isLoading: boolea
isLoading: isSubscriptionUsageDataLoading, isLoading: isSubscriptionUsageDataLoading,
mutate: mutateSubscriptionQuotaAndUsages, mutate: mutateSubscriptionQuotaAndUsages,
} = useSWR<NewSubscriptionUsageResponse, Error>( } = useSWR<NewSubscriptionUsageResponse, Error>(
isCloud && isDevFeaturesEnabled && tenantId && `/api/tenants/${tenantId}/subscription-usage`, isCloud && tenantId && `/api/tenants/${tenantId}/subscription-usage`,
async () => async () =>
cloudApi.get('/api/tenants/:tenantId/subscription-usage', { cloudApi.get('/api/tenants/:tenantId/subscription-usage', {
params: { tenantId }, params: { tenantId },

View file

@ -1,18 +0,0 @@
import useSWR from 'swr';
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
import { type SubscriptionUsage } from '@/cloud/types/router';
import { isCloud } from '@/consts/env';
/** @deprecated */
const useSubscriptionUsage = (tenantId: string) => {
const cloudApi = useCloudApi();
return useSWR<SubscriptionUsage, Error>(isCloud && `/api/tenants/${tenantId}/usage`, async () =>
cloudApi.get('/api/tenants/:tenantId/usage', {
params: { tenantId },
})
);
};
export default useSubscriptionUsage;

View file

@ -20,14 +20,12 @@ import {
} from '@/utils/subscription'; } from '@/utils/subscription';
type Props = { type Props = {
/** @deprecated No need to pass in this argument in new pricing model */
readonly activeUsers: number;
/** @deprecated No need to pass in this argument in new pricing model */ /** @deprecated No need to pass in this argument in new pricing model */
readonly currentPlan: SubscriptionPlan; readonly currentPlan: SubscriptionPlan;
readonly className?: string; readonly className?: string;
}; };
function MauLimitExceededNotification({ activeUsers, currentPlan, className }: Props) { function MauLimitExceededNotification({ currentPlan, className }: Props) {
const { currentTenantId } = useContext(TenantsContext); const { currentTenantId } = useContext(TenantsContext);
const { subscribe } = useSubscribe(); const { subscribe } = useSubscribe();
const { show } = useConfirmModal(); const { show } = useConfirmModal();
@ -52,7 +50,7 @@ function MauLimitExceededNotification({ activeUsers, currentPlan, className }: P
if ( if (
mauLimit === null || // Unlimited mauLimit === null || // Unlimited
(isDevFeaturesEnabled ? currentSubscriptionUsage.mauLimit : activeUsers) < mauLimit || currentSubscriptionUsage.mauLimit < mauLimit ||
!proPlan || !proPlan ||
!proSku !proSku
) { ) {

View file

@ -1,7 +1,7 @@
import { cond } from '@silverhand/essentials'; import { cond } from '@silverhand/essentials';
import { useContext, useMemo } from 'react'; import { useContext, useMemo } from 'react';
import { type SubscriptionUsage, type Subscription } from '@/cloud/types/router'; import { type Subscription } from '@/cloud/types/router';
import BillInfo from '@/components/BillInfo'; import BillInfo from '@/components/BillInfo';
import ChargeNotification from '@/components/ChargeNotification'; import ChargeNotification from '@/components/ChargeNotification';
import FormCard from '@/components/FormCard'; import FormCard from '@/components/FormCard';
@ -23,11 +23,9 @@ type Props = {
readonly subscription: Subscription; readonly subscription: Subscription;
/** @deprecated */ /** @deprecated */
readonly subscriptionPlan: SubscriptionPlan; readonly subscriptionPlan: SubscriptionPlan;
/** @deprecated */
readonly subscriptionUsage: SubscriptionUsage;
}; };
function CurrentPlan({ subscription, subscriptionPlan, subscriptionUsage }: Props) { function CurrentPlan({ subscription, subscriptionPlan }: Props) {
const { currentSku, currentSubscription, currentSubscriptionUsage, currentSubscriptionQuota } = const { currentSku, currentSubscription, currentSubscriptionUsage, currentSubscriptionQuota } =
useContext(SubscriptionDataContext); useContext(SubscriptionDataContext);
const { const {
@ -52,7 +50,7 @@ function CurrentPlan({ subscription, subscriptionPlan, subscriptionUsage }: Prop
}) })
: hasSurpassedQuotaLimit({ : hasSurpassedQuotaLimit({
quotaKey: 'tokenLimit', quotaKey: 'tokenLimit',
usage: subscriptionUsage.tokenUsage, usage: currentSubscriptionUsage.tokenLimit,
plan: subscriptionPlan, plan: subscriptionPlan,
}); });
@ -67,25 +65,12 @@ function CurrentPlan({ subscription, subscriptionPlan, subscriptionUsage }: Prop
</div> </div>
</div> </div>
<FormField title="subscription.plan_usage"> <FormField title="subscription.plan_usage">
<PlanUsage <PlanUsage currentSubscription={subscription} currentPlan={subscriptionPlan} />
currentSubscription={subscription}
currentPlan={subscriptionPlan}
subscriptionUsage={subscriptionUsage}
/>
</FormField> </FormField>
<FormField title="subscription.next_bill"> <FormField title="subscription.next_bill">
<BillInfo <BillInfo cost={upcomingCost} isManagePaymentVisible={Boolean(upcomingCost)} />
cost={isDevFeaturesEnabled ? upcomingCost : subscriptionUsage.cost}
isManagePaymentVisible={Boolean(
isDevFeaturesEnabled ? upcomingCost : subscriptionUsage.cost
)}
/>
</FormField> </FormField>
<MauLimitExceedNotification <MauLimitExceedNotification currentPlan={subscriptionPlan} className={styles.notification} />
activeUsers={subscriptionUsage.activeUsers}
currentPlan={subscriptionPlan}
className={styles.notification}
/>
<ChargeNotification <ChargeNotification
hasSurpassedLimit={hasTokenSurpassedLimit} hasSurpassedLimit={hasTokenSurpassedLimit}
quotaItemPhraseKey="tokens" quotaItemPhraseKey="tokens"

View file

@ -2,19 +2,14 @@ import { useContext } from 'react';
import PageMeta from '@/components/PageMeta'; import PageMeta from '@/components/PageMeta';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider'; import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import useSubscriptionUsage from '@/hooks/use-subscription-usage';
import { pickupFeaturedLogtoSkus, pickupFeaturedPlans } from '@/utils/subscription'; import { pickupFeaturedLogtoSkus, pickupFeaturedPlans } from '@/utils/subscription';
import Skeleton from '../components/Skeleton';
import CurrentPlan from './CurrentPlan'; import CurrentPlan from './CurrentPlan';
import PlanComparisonTable from './PlanComparisonTable'; import PlanComparisonTable from './PlanComparisonTable';
import SwitchPlanActionBar from './SwitchPlanActionBar'; import SwitchPlanActionBar from './SwitchPlanActionBar';
import styles from './index.module.scss'; import styles from './index.module.scss';
function Subscription() { function Subscription() {
const { currentTenantId } = useContext(TenantsContext);
const { const {
subscriptionPlans, subscriptionPlans,
currentPlan, currentPlan,
@ -24,31 +19,13 @@ function Subscription() {
onCurrentSubscriptionUpdated, onCurrentSubscriptionUpdated,
} = useContext(SubscriptionDataContext); } = useContext(SubscriptionDataContext);
const {
data: subscriptionUsage,
isLoading,
mutate: mutateSubscriptionUsage,
} = useSubscriptionUsage(currentTenantId);
const reservedPlans = pickupFeaturedPlans(subscriptionPlans); const reservedPlans = pickupFeaturedPlans(subscriptionPlans);
const reservedSkus = pickupFeaturedLogtoSkus(logtoSkus); const reservedSkus = pickupFeaturedLogtoSkus(logtoSkus);
if (isLoading) {
return <Skeleton />;
}
if (!subscriptionUsage) {
return null;
}
return ( return (
<div className={styles.container}> <div className={styles.container}>
<PageMeta titleKey={['tenants.tabs.subscription', 'tenants.title']} /> <PageMeta titleKey={['tenants.tabs.subscription', 'tenants.title']} />
<CurrentPlan <CurrentPlan subscription={currentSubscription} subscriptionPlan={currentPlan} />
subscription={currentSubscription}
subscriptionPlan={currentPlan}
subscriptionUsage={subscriptionUsage}
/>
<PlanComparisonTable /> <PlanComparisonTable />
<SwitchPlanActionBar <SwitchPlanActionBar
currentSubscriptionPlanId={currentSubscription.planId} currentSubscriptionPlanId={currentSubscription.planId}
@ -62,7 +39,6 @@ function Subscription() {
* need to manually trigger the usage update while the subscription plan is changed. * need to manually trigger the usage update while the subscription plan is changed.
*/ */
onCurrentSubscriptionUpdated(); onCurrentSubscriptionUpdated();
await mutateSubscriptionUsage();
}} }}
/> />
</div> </div>