mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
feat(console): add charge notification for add-on features (#5125)
This commit is contained in:
parent
ed1692959f
commit
b5bc374f2b
23 changed files with 112 additions and 10 deletions
packages
console/src
components
pages
ApiResources
Applications
Connectors/SignInExperienceSetupNotice
scss
phrases/src/locales
de/translation/admin-console/upsell
en/translation/admin-console/upsell
es/translation/admin-console/upsell
fr/translation/admin-console/upsell
it/translation/admin-console/upsell
ja/translation/admin-console/upsell
ko/translation/admin-console/upsell
pl-pl/translation/admin-console/upsell
pt-br/translation/admin-console/upsell
pt-pt/translation/admin-console/upsell
ru/translation/admin-console/upsell
tr-tr/translation/admin-console/upsell
zh-cn/translation/admin-console/upsell
zh-hk/translation/admin-console/upsell
zh-tw/translation/admin-console/upsell
47
packages/console/src/components/ChargeNotification/index.tsx
Normal file
47
packages/console/src/components/ChargeNotification/index.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { ReservedPlanId } from '@logto/schemas';
|
||||
import { type TFuncKey } from 'i18next';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
||||
import { TenantsContext } from '@/contexts/TenantsProvider';
|
||||
import DynamicT from '@/ds-components/DynamicT';
|
||||
import InlineNotification from '@/ds-components/InlineNotification';
|
||||
import useSubscriptionPlan from '@/hooks/use-subscription-plan';
|
||||
|
||||
type Props = {
|
||||
hasReachedLimit: boolean;
|
||||
notification?: TFuncKey<'translation', 'admin_console.upsell'>;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* We won't render it for free plan since we will not charge for free plan.
|
||||
*/
|
||||
function ChargeNotification({
|
||||
hasReachedLimit,
|
||||
notification = 'charge_notification_for_quota_limit',
|
||||
className,
|
||||
}: Props) {
|
||||
const { currentTenantId } = useContext(TenantsContext);
|
||||
const { data: currentPlan } = useSubscriptionPlan(currentTenantId);
|
||||
|
||||
if (
|
||||
// Todo @xiaoyijun [Pricing] Remove feature flag
|
||||
isDevFeaturesEnabled &&
|
||||
// No charge notification for free plan
|
||||
(!hasReachedLimit || currentPlan?.id === ReservedPlanId.Free)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<InlineNotification severity="error" className={className}>
|
||||
<DynamicT forKey={`upsell.${notification}`} />
|
||||
</InlineNotification>
|
||||
);
|
||||
}
|
||||
|
||||
export default ChargeNotification;
|
|
@ -49,7 +49,7 @@ function ListPage<
|
|||
<CardTitle {...title} />
|
||||
{createButton && <Button icon={<Plus />} type="primary" size="large" {...createButton} />}
|
||||
</div>
|
||||
{subHeader}
|
||||
{subHeader && <div className={pageLayout.subHeader}>{subHeader}</div>}
|
||||
<Table className={pageLayout.table} {...table} />
|
||||
{widgets}
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@ import ApiResourceDark from '@/assets/icons/api-resource-dark.svg';
|
|||
import ApiResource from '@/assets/icons/api-resource.svg';
|
||||
import ManagementApiResourceDark from '@/assets/icons/management-api-resource-dark.svg';
|
||||
import ManagementApiResource from '@/assets/icons/management-api-resource.svg';
|
||||
import ChargeNotification from '@/components/ChargeNotification';
|
||||
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
import ListPage from '@/components/ListPage';
|
||||
|
@ -17,6 +18,7 @@ import { ApiResourceDetailsTabs } from '@/consts/page-tabs';
|
|||
import CopyToClipboard from '@/ds-components/CopyToClipboard';
|
||||
import Tag from '@/ds-components/Tag';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApiResourcesUsage from '@/hooks/use-api-resources-usage';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
|
@ -39,7 +41,7 @@ const icons = {
|
|||
function ApiResources() {
|
||||
const { search } = useLocation();
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const { hasReachedLimit } = useApiResourcesUsage();
|
||||
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
||||
page: 1,
|
||||
});
|
||||
|
@ -76,6 +78,7 @@ function ApiResources() {
|
|||
});
|
||||
},
|
||||
}}
|
||||
subHeader={<ChargeNotification hasReachedLimit={hasReachedLimit} />}
|
||||
table={{
|
||||
rowGroups: [{ key: 'apiResources', data: apiResources }],
|
||||
rowIndexKey: 'id',
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.chargeNotification {
|
||||
margin: _.unit(4) 0 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import useSWR from 'swr';
|
|||
|
||||
import Plus from '@/assets/icons/plus.svg';
|
||||
import ApplicationIcon from '@/components/ApplicationIcon';
|
||||
import ChargeNotification from '@/components/ChargeNotification';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
import PageMeta from '@/components/PageMeta';
|
||||
import { defaultPageSize } from '@/consts';
|
||||
|
@ -15,6 +16,7 @@ import CopyToClipboard from '@/ds-components/CopyToClipboard';
|
|||
import OverlayScrollbar from '@/ds-components/OverlayScrollbar';
|
||||
import Table from '@/ds-components/Table';
|
||||
import type { RequestError } from '@/hooks/use-api';
|
||||
import useApplicationsUsage from '@/hooks/use-applications-usage';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import * as pageLayout from '@/scss/page-layout.module.scss';
|
||||
|
@ -35,6 +37,7 @@ function Applications() {
|
|||
const { match, navigate } = useTenantPathname();
|
||||
const isCreating = match(createApplicationPathname);
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { hasMachineToMachineAppsReachedLimit } = useApplicationsUsage();
|
||||
const [{ page }, updateSearchParameters] = useSearchParametersWatcher({
|
||||
page: 1,
|
||||
});
|
||||
|
@ -69,6 +72,11 @@ function Applications() {
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
<ChargeNotification
|
||||
hasReachedLimit={hasMachineToMachineAppsReachedLimit}
|
||||
notification="charge_notification_for_m2m_app_limit"
|
||||
className={styles.chargeNotification}
|
||||
/>
|
||||
{!isLoading && !applications?.length && (
|
||||
<OverlayScrollbar className={styles.guideLibraryContainer}>
|
||||
<CardTitle
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.notice {
|
||||
margin: _.unit(4) 0 0;
|
||||
}
|
|
@ -6,8 +6,6 @@ import InlineNotification from '@/ds-components/InlineNotification';
|
|||
import TextLink from '@/ds-components/TextLink';
|
||||
import useUserPreferences from '@/hooks/use-user-preferences';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
function SignInExperienceSetupNotice() {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const { data: connectors } = useSWR<ConnectorResponse[]>('api/connectors');
|
||||
|
@ -23,7 +21,6 @@ function SignInExperienceSetupNotice() {
|
|||
return (
|
||||
<InlineNotification
|
||||
action="general.got_it"
|
||||
className={styles.notice}
|
||||
onClick={() => {
|
||||
void update({ connectorSieNoticeConfirmed: true });
|
||||
}}
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.subHeader {
|
||||
margin: _.unit(4) 0 0;
|
||||
}
|
||||
|
||||
.table {
|
||||
flex: 1;
|
||||
margin-top: _.unit(4);
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ const upsell = {
|
|||
'You have reached your quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ const upsell = {
|
|||
/** UNTRANSLATED */
|
||||
charge_notification_for_token_limit:
|
||||
'You have reached your {{value}}M token quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
/** UNTRANSLATED */
|
||||
charge_notification_for_m2m_app_limit:
|
||||
'You have reached your machine-to-machine quota limit. We may add charges for features that go beyond your quota limit as add-ons, once we finalize the prices.',
|
||||
paywall,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue