mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(console,core): support hidding charge notification on read (#5151)
This commit is contained in:
parent
aca6695fc3
commit
a6f12307b3
4 changed files with 52 additions and 11 deletions
|
@ -1,5 +1,5 @@
|
||||||
import { ReservedPlanId } from '@logto/schemas';
|
import { type AdminConsoleData, ReservedPlanId } from '@logto/schemas';
|
||||||
import { cond } from '@silverhand/essentials';
|
import { cond, type Truthy } from '@silverhand/essentials';
|
||||||
import { type TFuncKey } from 'i18next';
|
import { type TFuncKey } from 'i18next';
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
@ -8,27 +8,44 @@ import { pricingLink } from '@/consts';
|
||||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||||
import InlineNotification from '@/ds-components/InlineNotification';
|
import InlineNotification from '@/ds-components/InlineNotification';
|
||||||
import TextLink from '@/ds-components/TextLink';
|
import TextLink from '@/ds-components/TextLink';
|
||||||
|
import useConfigs from '@/hooks/use-configs';
|
||||||
import useSubscriptionPlan from '@/hooks/use-subscription-plan';
|
import useSubscriptionPlan from '@/hooks/use-subscription-plan';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
hasSurpassedLimit: boolean;
|
hasSurpassedLimit: boolean;
|
||||||
quotaItem: TFuncKey<'translation', 'admin_console.upsell.add_on_quota_item'>;
|
quotaItemPhraseKey: TFuncKey<'translation', 'admin_console.upsell.add_on_quota_item'>;
|
||||||
quotaLimit?: number;
|
quotaLimit?: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
/**
|
||||||
|
* The key of the flag in `checkedChargeNotification` config from the AdminConsoleData.
|
||||||
|
* Used to determine whether the notification has been checked.
|
||||||
|
* @see{@link AdminConsoleData}
|
||||||
|
*/
|
||||||
|
checkedFlagKey: keyof Truthy<AdminConsoleData['checkedChargeNotification']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Charge notification for add-on quota limit features
|
* Charge notification for add-on quota limit features
|
||||||
*
|
*
|
||||||
* CAUTION: This notification will be rendered only when the tenant's subscription plan is a paid plan.
|
* CAUTION:
|
||||||
* We won't render it for free plan since we will not charge for free plan.
|
* - This notification will be rendered only when the tenant's subscription plan is a paid plan. We won't render it for free plan since we will not charge for free plan.
|
||||||
|
* - If the notification has been marked as checked, it will not be rendered.
|
||||||
*/
|
*/
|
||||||
function ChargeNotification({ hasSurpassedLimit, quotaItem, quotaLimit, className }: Props) {
|
function ChargeNotification({
|
||||||
|
hasSurpassedLimit,
|
||||||
|
quotaItemPhraseKey,
|
||||||
|
quotaLimit,
|
||||||
|
className,
|
||||||
|
checkedFlagKey,
|
||||||
|
}: Props) {
|
||||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' });
|
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell' });
|
||||||
const { currentTenantId } = useContext(TenantsContext);
|
const { currentTenantId } = useContext(TenantsContext);
|
||||||
const { data: currentPlan } = useSubscriptionPlan(currentTenantId);
|
const { data: currentPlan } = useSubscriptionPlan(currentTenantId);
|
||||||
|
const { configs, updateConfigs } = useConfigs();
|
||||||
|
const checkedChargeNotification = configs?.checkedChargeNotification;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
Boolean(checkedChargeNotification?.[checkedFlagKey]) ||
|
||||||
!hasSurpassedLimit ||
|
!hasSurpassedLimit ||
|
||||||
// No charge notification for free plan
|
// No charge notification for free plan
|
||||||
currentPlan?.id === ReservedPlanId.Free
|
currentPlan?.id === ReservedPlanId.Free
|
||||||
|
@ -37,13 +54,26 @@ function ChargeNotification({ hasSurpassedLimit, quotaItem, quotaLimit, classNam
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InlineNotification className={className}>
|
<InlineNotification
|
||||||
|
className={className}
|
||||||
|
action="general.got_it"
|
||||||
|
onClick={() => {
|
||||||
|
void updateConfigs({
|
||||||
|
checkedChargeNotification: {
|
||||||
|
...checkedChargeNotification,
|
||||||
|
[checkedFlagKey]: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Trans components={{ a: <TextLink href={pricingLink} targetBlank="noopener" /> }}>
|
<Trans components={{ a: <TextLink href={pricingLink} targetBlank="noopener" /> }}>
|
||||||
{t('charge_notification_for_quota_limit', {
|
{t('charge_notification_for_quota_limit', {
|
||||||
item: t(`add_on_quota_item.${quotaItem}`, {
|
item: t(`add_on_quota_item.${quotaItemPhraseKey}`, {
|
||||||
...cond(
|
...cond(
|
||||||
// Note: tokens use 'M' as unit
|
// Note: tokens use 'M' as unit
|
||||||
quotaLimit && { limit: quotaItem === 'tokens' ? quotaLimit / 1_000_000 : quotaLimit }
|
quotaLimit && {
|
||||||
|
limit: quotaItemPhraseKey === 'tokens' ? quotaLimit / 1_000_000 : quotaLimit,
|
||||||
|
}
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -83,7 +83,11 @@ function ApiResources() {
|
||||||
isCloud &&
|
isCloud &&
|
||||||
// Todo @xiaoyijun [Pricing] Remove feature flag
|
// Todo @xiaoyijun [Pricing] Remove feature flag
|
||||||
isDevFeaturesEnabled && (
|
isDevFeaturesEnabled && (
|
||||||
<ChargeNotification hasSurpassedLimit={hasSurpassedLimit} quotaItem="api_resource" />
|
<ChargeNotification
|
||||||
|
hasSurpassedLimit={hasSurpassedLimit}
|
||||||
|
quotaItemPhraseKey="api_resource"
|
||||||
|
checkedFlagKey="apiResource"
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
table={{
|
table={{
|
||||||
|
|
|
@ -77,8 +77,9 @@ function Applications() {
|
||||||
{isCloud && isDevFeaturesEnabled && (
|
{isCloud && isDevFeaturesEnabled && (
|
||||||
<ChargeNotification
|
<ChargeNotification
|
||||||
hasSurpassedLimit={hasMachineToMachineAppsSurpassedLimit}
|
hasSurpassedLimit={hasMachineToMachineAppsSurpassedLimit}
|
||||||
quotaItem="machine_to_machine"
|
quotaItemPhraseKey="machine_to_machine"
|
||||||
className={styles.chargeNotification}
|
className={styles.chargeNotification}
|
||||||
|
checkedFlagKey="machineToMachineApp"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{!isLoading && !applications?.length && (
|
{!isLoading && !applications?.length && (
|
||||||
|
|
|
@ -58,6 +58,12 @@ export const adminConsoleDataGuard = z.object({
|
||||||
readAt: z.number().optional(),
|
readAt: z.number().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
|
checkedChargeNotification: z
|
||||||
|
.object({
|
||||||
|
apiResource: z.boolean().optional(),
|
||||||
|
machineToMachineApp: z.boolean().optional(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AdminConsoleData = z.infer<typeof adminConsoleDataGuard>;
|
export type AdminConsoleData = z.infer<typeof adminConsoleDataGuard>;
|
||||||
|
|
Loading…
Reference in a new issue