mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor: move has{Reach,Surpass}Limit method to subscription context (#6664)
* refactor: move has{Reach,Surpass}Limit method to subscription context * chore: update code
This commit is contained in:
parent
c6f59cb0f8
commit
4dc1f82195
15 changed files with 152 additions and 190 deletions
|
@ -6,7 +6,6 @@ import QuotaGuardFooter from '@/components/QuotaGuardFooter';
|
|||
import SkuName from '@/components/SkuName';
|
||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||
import Button from '@/ds-components/Button';
|
||||
import { hasReachedSubscriptionQuotaLimit } from '@/utils/quota';
|
||||
|
||||
type Props = {
|
||||
readonly isCreatingSocialConnector: boolean;
|
||||
|
@ -18,15 +17,11 @@ function Footer({ isCreatingSocialConnector, isCreateButtonDisabled, onClickCrea
|
|||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.paywall' });
|
||||
const {
|
||||
currentSubscription: { planId, isEnterprisePlan },
|
||||
currentSubscriptionUsage,
|
||||
currentSubscriptionQuota,
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
|
||||
const isSocialConnectorsReachLimit = hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'socialConnectorsLimit',
|
||||
usage: currentSubscriptionUsage.socialConnectorsLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const isSocialConnectorsReachLimit = hasReachedSubscriptionQuotaLimit('socialConnectorsLimit');
|
||||
|
||||
if (isCreatingSocialConnector && isSocialConnectorsReachLimit) {
|
||||
return (
|
||||
|
|
|
@ -2,12 +2,17 @@ import { conditional, joinPath } from '@silverhand/essentials';
|
|||
import { useContext, useRef } from 'react';
|
||||
import { Navigate, Outlet, useParams } from 'react-router-dom';
|
||||
|
||||
import { type NewSubscriptionCountBasedUsage } from '@/cloud/types/router';
|
||||
import AppLoading from '@/components/AppLoading';
|
||||
import Topbar from '@/components/Topbar';
|
||||
import { isCloud } from '@/consts/env';
|
||||
import SubscriptionDataProvider from '@/contexts/SubscriptionDataProvider';
|
||||
import useNewSubscriptionData from '@/contexts/SubscriptionDataProvider/use-new-subscription-data';
|
||||
import useSubscriptionData from '@/contexts/SubscriptionDataProvider/use-subscription-data';
|
||||
import {
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
} from '@/contexts/SubscriptionDataProvider/utils';
|
||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||
import useScroll from '@/hooks/use-scroll';
|
||||
import useUserPreferences from '@/hooks/use-user-preferences';
|
||||
|
@ -42,9 +47,29 @@ export default function AppContent() {
|
|||
|
||||
return (
|
||||
<SubscriptionDataProvider
|
||||
subscriptionData={{
|
||||
subscriptionDataAndUtils={{
|
||||
...subscriptionDta,
|
||||
...newSubscriptionData,
|
||||
hasSurpassedSubscriptionQuotaLimit: <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
quotaKey: T,
|
||||
usage?: NewSubscriptionCountBasedUsage[T]
|
||||
) =>
|
||||
hasSurpassedSubscriptionQuotaLimit({
|
||||
quotaKey,
|
||||
usage,
|
||||
subscriptionUsage: newSubscriptionData.currentSubscriptionUsage,
|
||||
subscriptionQuota: newSubscriptionData.currentSubscriptionQuota,
|
||||
}),
|
||||
hasReachedSubscriptionQuotaLimit: <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
quotaKey: T,
|
||||
usage?: NewSubscriptionCountBasedUsage[T]
|
||||
) =>
|
||||
hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey,
|
||||
usage,
|
||||
subscriptionUsage: newSubscriptionData.currentSubscriptionUsage,
|
||||
subscriptionQuota: newSubscriptionData.currentSubscriptionQuota,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<div className={styles.app}>
|
||||
|
|
|
@ -32,16 +32,18 @@ export const SubscriptionDataContext = createContext<FullContext>({
|
|||
currentSubscriptionRoleScopeUsage: {},
|
||||
mutateSubscriptionQuotaAndUsages: noop,
|
||||
/* ==== For new pricing model ==== */
|
||||
hasSurpassedSubscriptionQuotaLimit: () => false,
|
||||
hasReachedSubscriptionQuotaLimit: () => false,
|
||||
});
|
||||
|
||||
type Props = {
|
||||
readonly subscriptionData: FullContext;
|
||||
readonly subscriptionDataAndUtils: FullContext;
|
||||
readonly children: ReactNode;
|
||||
};
|
||||
|
||||
function SubscriptionDataProvider({ children, subscriptionData }: Props) {
|
||||
function SubscriptionDataProvider({ children, subscriptionDataAndUtils }: Props) {
|
||||
return (
|
||||
<SubscriptionDataContext.Provider value={subscriptionData}>
|
||||
<SubscriptionDataContext.Provider value={subscriptionDataAndUtils}>
|
||||
{children}
|
||||
</SubscriptionDataContext.Provider>
|
||||
);
|
||||
|
|
|
@ -12,6 +12,13 @@ export type Context = {
|
|||
onCurrentSubscriptionUpdated: (subscription?: Subscription) => void;
|
||||
};
|
||||
|
||||
export type SubscriptionUsageOptions<T extends keyof NewSubscriptionCountBasedUsage> = {
|
||||
quotaKey: T;
|
||||
subscriptionUsage: NewSubscriptionCountBasedUsage;
|
||||
subscriptionQuota: NewSubscriptionQuota;
|
||||
usage?: NewSubscriptionCountBasedUsage[T];
|
||||
};
|
||||
|
||||
type NewSubscriptionSupplementContext = {
|
||||
logtoSkus: LogtoSkuResponse[];
|
||||
currentSku: LogtoSkuResponse;
|
||||
|
@ -23,6 +30,19 @@ type NewSubscriptionSupplementContext = {
|
|||
mutateSubscriptionQuotaAndUsages: () => void;
|
||||
};
|
||||
|
||||
type NewSubscriptionResourceStatus = {
|
||||
hasSurpassedSubscriptionQuotaLimit: <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
quotaKey: T,
|
||||
usage?: NewSubscriptionCountBasedUsage[T]
|
||||
) => boolean;
|
||||
hasReachedSubscriptionQuotaLimit: <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
quotaKey: T,
|
||||
usage?: NewSubscriptionCountBasedUsage[T]
|
||||
) => boolean;
|
||||
};
|
||||
|
||||
export type NewSubscriptionContext = Context & NewSubscriptionSupplementContext;
|
||||
|
||||
export type FullContext = Context & NewSubscriptionSupplementContext;
|
||||
export type FullContext = Context &
|
||||
NewSubscriptionSupplementContext &
|
||||
NewSubscriptionResourceStatus;
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { type NewSubscriptionCountBasedUsage } from '@/cloud/types/router';
|
||||
import { isCloud } from '@/consts/env';
|
||||
|
||||
import { type SubscriptionUsageOptions } from './types';
|
||||
|
||||
/* === For new pricing model === */
|
||||
const isSubscriptionUsageWithInLimit = <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
{ quotaKey, subscriptionUsage, subscriptionQuota, usage }: SubscriptionUsageOptions<T>,
|
||||
inclusive = true
|
||||
) => {
|
||||
// No limitations for OSS version
|
||||
if (!isCloud) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes we need to manually retrieve usage to overwrite the usage in subscriptionUsage.
|
||||
* For example, for the usage of `scopesPerResourceLimit`, `subscriptionUsage.scopesPerResourceLimit` records the largest value among all resource scopes.
|
||||
* However, when operating on resources in practice, we need to know the specific usage of scopes for the current resource. In this case, we need to manually calculate the value of scopes for the current resource before calling the function.
|
||||
*/
|
||||
const usageValue = usage ?? subscriptionUsage[quotaKey];
|
||||
const quotaValue = subscriptionQuota[quotaKey];
|
||||
|
||||
// Unlimited
|
||||
if (quotaValue === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof quotaValue === 'boolean') {
|
||||
return quotaValue;
|
||||
}
|
||||
|
||||
return inclusive ? usageValue <= quotaValue : usageValue < quotaValue;
|
||||
};
|
||||
|
||||
export const hasSurpassedSubscriptionQuotaLimit = <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
options: SubscriptionUsageOptions<T>
|
||||
) => !isSubscriptionUsageWithInLimit(options);
|
||||
|
||||
export const hasReachedSubscriptionQuotaLimit = <T extends keyof NewSubscriptionCountBasedUsage>(
|
||||
options: SubscriptionUsageOptions<T>
|
||||
) => !isSubscriptionUsageWithInLimit(options, false);
|
||||
/* === For new pricing model === */
|
|
@ -1,34 +1,14 @@
|
|||
import { useContext, useMemo } from 'react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||
import {
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} from '@/utils/quota';
|
||||
|
||||
const useApiResourcesUsage = () => {
|
||||
const { currentSubscriptionQuota, currentSubscriptionUsage } =
|
||||
const { hasReachedSubscriptionQuotaLimit, hasSurpassedSubscriptionQuotaLimit } =
|
||||
useContext(SubscriptionDataContext);
|
||||
|
||||
const hasReachedLimit = useMemo(
|
||||
() =>
|
||||
hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'resourcesLimit',
|
||||
usage: currentSubscriptionUsage.resourcesLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
}),
|
||||
[currentSubscriptionQuota, currentSubscriptionUsage.resourcesLimit]
|
||||
);
|
||||
const hasReachedLimit = hasReachedSubscriptionQuotaLimit('resourcesLimit');
|
||||
|
||||
const hasSurpassedLimit = useMemo(
|
||||
() =>
|
||||
hasSurpassedSubscriptionQuotaLimit({
|
||||
quotaKey: 'resourcesLimit',
|
||||
usage: currentSubscriptionUsage.resourcesLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
}),
|
||||
[currentSubscriptionQuota, currentSubscriptionUsage.resourcesLimit]
|
||||
);
|
||||
const hasSurpassedLimit = hasSurpassedSubscriptionQuotaLimit('resourcesLimit');
|
||||
|
||||
return {
|
||||
hasReachedLimit,
|
||||
|
|
|
@ -1,54 +1,22 @@
|
|||
import { useContext, useMemo } from 'react';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||
import {
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} from '@/utils/quota';
|
||||
|
||||
const useApplicationsUsage = () => {
|
||||
const { currentSubscriptionQuota, currentSubscriptionUsage } =
|
||||
const { hasReachedSubscriptionQuotaLimit, hasSurpassedSubscriptionQuotaLimit } =
|
||||
useContext(SubscriptionDataContext);
|
||||
|
||||
const hasMachineToMachineAppsReachedLimit = useMemo(
|
||||
() =>
|
||||
hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'machineToMachineLimit',
|
||||
usage: currentSubscriptionUsage.machineToMachineLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
}),
|
||||
[currentSubscriptionUsage.machineToMachineLimit, currentSubscriptionQuota]
|
||||
const hasMachineToMachineAppsReachedLimit =
|
||||
hasReachedSubscriptionQuotaLimit('machineToMachineLimit');
|
||||
|
||||
const hasMachineToMachineAppsSurpassedLimit =
|
||||
hasSurpassedSubscriptionQuotaLimit('machineToMachineLimit');
|
||||
|
||||
const hasThirdPartyAppsReachedLimit = hasReachedSubscriptionQuotaLimit(
|
||||
'thirdPartyApplicationsLimit'
|
||||
);
|
||||
|
||||
const hasMachineToMachineAppsSurpassedLimit = useMemo(
|
||||
() =>
|
||||
hasSurpassedSubscriptionQuotaLimit({
|
||||
quotaKey: 'machineToMachineLimit',
|
||||
usage: currentSubscriptionUsage.machineToMachineLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
}),
|
||||
[currentSubscriptionUsage.machineToMachineLimit, currentSubscriptionQuota]
|
||||
);
|
||||
|
||||
const hasThirdPartyAppsReachedLimit = useMemo(
|
||||
() =>
|
||||
hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'thirdPartyApplicationsLimit',
|
||||
usage: currentSubscriptionUsage.thirdPartyApplicationsLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
}),
|
||||
[currentSubscriptionUsage.thirdPartyApplicationsLimit, currentSubscriptionQuota]
|
||||
);
|
||||
|
||||
const hasAppsReachedLimit = useMemo(
|
||||
() =>
|
||||
hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'applicationsLimit',
|
||||
usage: currentSubscriptionUsage.applicationsLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
}),
|
||||
[currentSubscriptionUsage.applicationsLimit, currentSubscriptionQuota]
|
||||
);
|
||||
const hasAppsReachedLimit = hasReachedSubscriptionQuotaLimit('applicationsLimit');
|
||||
|
||||
return {
|
||||
hasMachineToMachineAppsReachedLimit,
|
||||
|
|
|
@ -16,7 +16,6 @@ import TextInput from '@/ds-components/TextInput';
|
|||
import useApi from '@/hooks/use-api';
|
||||
import modalStyles from '@/scss/modal.module.scss';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
import { hasReachedSubscriptionQuotaLimit } from '@/utils/quota';
|
||||
|
||||
type Props = {
|
||||
readonly resourceId: string;
|
||||
|
@ -32,6 +31,7 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop
|
|||
currentSubscriptionQuota,
|
||||
currentSubscriptionResourceScopeUsage,
|
||||
currentSubscription: { planId, isEnterprisePlan },
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
|
@ -57,11 +57,10 @@ function CreatePermissionModal({ resourceId, totalResourceCount, onClose }: Prop
|
|||
})
|
||||
);
|
||||
|
||||
const isScopesPerResourceReachLimit = hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'scopesPerResourceLimit',
|
||||
usage: currentSubscriptionResourceScopeUsage[resourceId] ?? 0,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const isScopesPerResourceReachLimit = hasReachedSubscriptionQuotaLimit(
|
||||
'scopesPerResourceLimit',
|
||||
currentSubscriptionResourceScopeUsage[resourceId] ?? 0
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
|
|
|
@ -14,7 +14,6 @@ import FormField from '@/ds-components/FormField';
|
|||
import ModalLayout from '@/ds-components/ModalLayout';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import modalStyles from '@/scss/modal.module.scss';
|
||||
import { hasSurpassedSubscriptionQuotaLimit } from '@/utils/quota';
|
||||
|
||||
type Props = {
|
||||
readonly roleId: string;
|
||||
|
@ -28,6 +27,7 @@ function AssignPermissionsModal({ roleId, roleType, onClose }: Props) {
|
|||
currentSubscription: { planId, isEnterprisePlan },
|
||||
currentSubscriptionRoleScopeUsage,
|
||||
currentSubscriptionQuota,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [scopes, setScopes] = useState<ScopeResponse[]>([]);
|
||||
|
@ -52,11 +52,10 @@ function AssignPermissionsModal({ roleId, roleType, onClose }: Props) {
|
|||
}
|
||||
};
|
||||
|
||||
const shouldBlockScopeAssignment = hasSurpassedSubscriptionQuotaLimit({
|
||||
quotaKey: 'scopesPerRoleLimit',
|
||||
usage: (currentSubscriptionRoleScopeUsage[roleId] ?? 0) + scopes.length,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const shouldBlockScopeAssignment = hasSurpassedSubscriptionQuotaLimit(
|
||||
'scopesPerRoleLimit',
|
||||
(currentSubscriptionRoleScopeUsage[roleId] ?? 0) + scopes.length
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { RoleType } from '@logto/schemas';
|
||||
import { RoleType, type ScopeResponse } from '@logto/schemas';
|
||||
import { useContext } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -7,39 +7,31 @@ import QuotaGuardFooter from '@/components/QuotaGuardFooter';
|
|||
import SkuName from '@/components/SkuName';
|
||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||
import Button from '@/ds-components/Button';
|
||||
import {
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} from '@/utils/quota';
|
||||
|
||||
type Props = {
|
||||
readonly roleType: RoleType;
|
||||
readonly scopes?: ScopeResponse[];
|
||||
readonly isCreating: boolean;
|
||||
readonly onClickCreate: () => void;
|
||||
};
|
||||
|
||||
function Footer({ roleType, isCreating, onClickCreate }: Props) {
|
||||
function Footer({ roleType, scopes, isCreating, onClickCreate }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const {
|
||||
currentSubscription: { planId, isEnterprisePlan },
|
||||
currentSubscriptionQuota,
|
||||
currentSubscriptionUsage,
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
|
||||
const hasRoleReachedLimit = hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: roleType === RoleType.User ? 'userRolesLimit' : 'machineToMachineRolesLimit',
|
||||
usage:
|
||||
roleType === RoleType.User
|
||||
? currentSubscriptionUsage.userRolesLimit
|
||||
: currentSubscriptionUsage.machineToMachineRolesLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const hasRoleReachedLimit = hasReachedSubscriptionQuotaLimit(
|
||||
roleType === RoleType.User ? 'userRolesLimit' : 'machineToMachineRolesLimit'
|
||||
);
|
||||
|
||||
const hasScopesPerRoleSurpassedLimit = hasSurpassedSubscriptionQuotaLimit({
|
||||
quotaKey: 'scopesPerRoleLimit',
|
||||
usage: currentSubscriptionUsage.scopesPerRoleLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const hasScopesPerRoleSurpassedLimit = hasSurpassedSubscriptionQuotaLimit(
|
||||
'scopesPerRoleLimit',
|
||||
scopes?.length ?? 0
|
||||
);
|
||||
|
||||
if (hasRoleReachedLimit || hasScopesPerRoleSurpassedLimit) {
|
||||
return (
|
||||
|
|
|
@ -77,7 +77,12 @@ function CreateRoleForm({ onClose }: Props) {
|
|||
}}
|
||||
size="large"
|
||||
footer={
|
||||
<Footer roleType={watch('type')} isCreating={isSubmitting} onClickCreate={onSubmit} />
|
||||
<Footer
|
||||
roleType={watch('type')}
|
||||
scopes={watch('scopes')}
|
||||
isCreating={isSubmitting}
|
||||
onClickCreate={onSubmit}
|
||||
/>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
|
|
|
@ -19,7 +19,6 @@ import TextLink from '@/ds-components/TextLink';
|
|||
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';
|
||||
|
@ -44,9 +43,8 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
|
|||
const { show } = useConfirmModal();
|
||||
const {
|
||||
currentSubscription: { planId, isAddOnAvailable, isEnterprisePlan },
|
||||
currentSubscriptionQuota,
|
||||
currentSubscriptionUsage: { tenantMembersLimit },
|
||||
mutateSubscriptionQuotaAndUsages,
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
const {
|
||||
data: { tenantMembersUpsellNoticeAcknowledged },
|
||||
|
@ -82,11 +80,7 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
|
|||
[t]
|
||||
);
|
||||
|
||||
const hasTenantMembersReachedLimit = hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'tenantMembersLimit',
|
||||
usage: tenantMembersLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const hasTenantMembersReachedLimit = hasReachedSubscriptionQuotaLimit('tenantMembersLimit');
|
||||
|
||||
const onSubmit = handleSubmit(async ({ emails, role }) => {
|
||||
if (role === TenantRole.Admin) {
|
||||
|
|
|
@ -1,38 +1,22 @@
|
|||
import { useContext, useMemo } from 'react';
|
||||
|
||||
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
|
||||
import {
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} from '@/utils/quota';
|
||||
|
||||
const useTenantMembersUsage = () => {
|
||||
const { currentSubscriptionUsage, currentSubscriptionQuota } =
|
||||
useContext(SubscriptionDataContext);
|
||||
const {
|
||||
currentSubscriptionUsage,
|
||||
currentSubscriptionQuota,
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
hasSurpassedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
|
||||
const usage = useMemo(() => {
|
||||
return currentSubscriptionUsage.tenantMembersLimit;
|
||||
}, [currentSubscriptionUsage.tenantMembersLimit]);
|
||||
|
||||
const hasTenantMembersReachedLimit = useMemo(
|
||||
() =>
|
||||
hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'tenantMembersLimit',
|
||||
quota: currentSubscriptionQuota,
|
||||
usage: currentSubscriptionUsage.tenantMembersLimit,
|
||||
}),
|
||||
[currentSubscriptionQuota, currentSubscriptionUsage.tenantMembersLimit]
|
||||
);
|
||||
const hasTenantMembersReachedLimit = hasReachedSubscriptionQuotaLimit('tenantMembersLimit');
|
||||
|
||||
const hasTenantMembersSurpassedLimit = useMemo(
|
||||
() =>
|
||||
hasSurpassedSubscriptionQuotaLimit({
|
||||
quotaKey: 'tenantMembersLimit',
|
||||
quota: currentSubscriptionQuota,
|
||||
usage: currentSubscriptionUsage.tenantMembersLimit,
|
||||
}),
|
||||
[currentSubscriptionQuota, currentSubscriptionUsage.tenantMembersLimit]
|
||||
);
|
||||
const hasTenantMembersSurpassedLimit = hasSurpassedSubscriptionQuotaLimit('tenantMembersLimit');
|
||||
|
||||
return {
|
||||
hasTenantMembersReachedLimit,
|
||||
|
|
|
@ -12,7 +12,6 @@ import Button from '@/ds-components/Button';
|
|||
import ModalLayout from '@/ds-components/ModalLayout';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
import { hasReachedSubscriptionQuotaLimit } from '@/utils/quota';
|
||||
|
||||
type Props = {
|
||||
readonly onClose: (createdHook?: Hook) => void;
|
||||
|
@ -28,16 +27,12 @@ type CreateHookPayload = Pick<CreateHook, 'name'> & {
|
|||
function CreateForm({ onClose }: Props) {
|
||||
const {
|
||||
currentSubscription: { planId, isEnterprisePlan },
|
||||
currentSubscriptionQuota,
|
||||
currentSubscriptionUsage,
|
||||
hasReachedSubscriptionQuotaLimit,
|
||||
} = useContext(SubscriptionDataContext);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const shouldBlockCreation = hasReachedSubscriptionQuotaLimit({
|
||||
quotaKey: 'hooksLimit',
|
||||
usage: currentSubscriptionUsage.hooksLimit,
|
||||
quota: currentSubscriptionQuota,
|
||||
});
|
||||
const shouldBlockCreation = hasReachedSubscriptionQuotaLimit('hooksLimit');
|
||||
|
||||
const formMethods = useForm<BasicWebhookFormType>();
|
||||
const {
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
import { type NewSubscriptionQuota } from '@/cloud/types/router';
|
||||
import { isCloud } from '@/consts/env';
|
||||
|
||||
/* === For new pricing model === */
|
||||
type SubscriptionUsageOptions = {
|
||||
quotaKey: keyof NewSubscriptionQuota;
|
||||
usage: number;
|
||||
quota: NewSubscriptionQuota;
|
||||
};
|
||||
|
||||
const isSubscriptionUsageWithInLimit = (
|
||||
{ quotaKey, usage, quota }: SubscriptionUsageOptions,
|
||||
inclusive = true
|
||||
) => {
|
||||
// No limitations for OSS version
|
||||
if (!isCloud) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const quotaValue = quota[quotaKey];
|
||||
|
||||
// Unlimited
|
||||
if (quotaValue === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof quotaValue === 'boolean') {
|
||||
return quotaValue;
|
||||
}
|
||||
|
||||
return inclusive ? usage <= quotaValue : usage < quotaValue;
|
||||
};
|
||||
|
||||
export const hasSurpassedSubscriptionQuotaLimit = (options: SubscriptionUsageOptions) =>
|
||||
!isSubscriptionUsageWithInLimit(options);
|
||||
|
||||
export const hasReachedSubscriptionQuotaLimit = (options: SubscriptionUsageOptions) =>
|
||||
!isSubscriptionUsageWithInLimit(options, false);
|
||||
/* === For new pricing model === */
|
Loading…
Reference in a new issue