0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

refactor(console): update featured plan content in tenant creation modal (#5418)

This commit is contained in:
Xiao Yijun 2024-02-22 11:37:27 +08:00 committed by GitHub
parent 168ddc5927
commit 3df1994bb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 480 additions and 130 deletions

View file

@ -0,0 +1,33 @@
@use '@/scss/underscore' as _;
.list {
flex: 1;
margin-block: 0;
padding-inline: 0;
padding-bottom: _.unit(8);
list-style: none;
> li {
display: flex;
font: var(--font-body-2);
align-items: center;
gap: _.unit(2);
.icon {
width: 16px;
height: 16px;
&.failed {
color: var(--color-on-error-container);
}
&.success {
color: var(--color-on-success-container);
}
}
&:not(:first-child) {
margin-top: _.unit(3);
}
}
}

View file

@ -0,0 +1,34 @@
import classNames from 'classnames';
import Failed from '@/assets/icons/failed.svg';
import Success from '@/assets/icons/success.svg';
import * as styles from './index.module.scss';
import useFeaturedPlanContent from './use-featured-plan-content';
type Props = {
planId: string;
};
function FeaturedPlanContent({ planId }: Props) {
const contentData = useFeaturedPlanContent(planId);
return (
<ul className={styles.list}>
{contentData.map(({ title, isAvailable }) => {
return (
<li key={title}>
{isAvailable ? (
<Success className={classNames(styles.icon, styles.success)} />
) : (
<Failed className={classNames(styles.icon, styles.failed)} />
)}
{title}
</li>
);
})}
</ul>
);
}
export default FeaturedPlanContent;

View file

@ -0,0 +1,66 @@
import { ReservedPlanId } from '@logto/schemas';
import { cond } from '@silverhand/essentials';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
type ContentData = {
title: string;
isAvailable: boolean;
};
const useFeaturedPlanContent = (planId: string) => {
const { t } = useTranslation(undefined, {
keyPrefix: 'admin_console.upsell.featured_plan_content',
});
const contentData: ContentData[] = useMemo(() => {
const isFreePlan = planId === ReservedPlanId.Free;
const planPhraseKey = isFreePlan ? 'free_plan' : 'pro_plan';
return [
{
title: t(`mau.${planPhraseKey}`, { ...cond(isFreePlan && { count: 50_000 }) }),
isAvailable: true,
},
{
title: t(`m2m.${planPhraseKey}`, { ...cond(isFreePlan && { count: 1 }) }),
isAvailable: true,
},
{
title: t('third_party_apps'),
isAvailable: !isFreePlan,
},
{
title: t('mfa'),
isAvailable: !isFreePlan,
},
{
title: t('sso'),
isAvailable: !isFreePlan,
},
{
title: t(`role_and_permissions.${planPhraseKey}`, {
...cond(
isFreePlan && {
roleCount: 1,
permissionCount: 1,
}
),
}),
isAvailable: true,
},
{
title: t('organizations'),
isAvailable: !isFreePlan,
},
{
title: t('audit_logs', { count: isFreePlan ? 3 : 14 }),
isAvailable: true,
},
];
}, [t, planId]);
return contentData;
};
export default useFeaturedPlanContent;

View file

@ -1,14 +0,0 @@
@use '@/scss/underscore' as _;
.icon {
width: 16px;
height: 16px;
&.notCapable {
color: var(--color-error);
}
&.capable {
color: var(--color-on-success-container);
}
}

View file

@ -1,45 +0,0 @@
import { cond } from '@silverhand/essentials';
import classNames from 'classnames';
import Failed from '@/assets/icons/failed.svg';
import Success from '@/assets/icons/success.svg';
import QuotaListItem from '@/components/PlanQuotaList/QuotaListItem';
import DynamicT from '@/ds-components/DynamicT';
import Tag from '@/ds-components/Tag';
import { type SubscriptionPlanQuota } from '@/types/subscriptions';
import * as styles from './index.module.scss';
type Props = {
quotaKey: keyof SubscriptionPlanQuota;
quotaValue: SubscriptionPlanQuota[keyof SubscriptionPlanQuota];
isAddOnQuota: boolean;
isComingSoonTagVisible: boolean;
};
function FeaturedQuotaItem({ quotaKey, quotaValue, isAddOnQuota, isComingSoonTagVisible }: Props) {
const isNotCapable = quotaValue === 0 || quotaValue === false;
const Icon = isNotCapable ? Failed : Success;
return (
<QuotaListItem
quotaKey={quotaKey}
quotaValue={quotaValue}
isAddOn={isAddOnQuota}
icon={
<Icon
className={classNames(styles.icon, isNotCapable ? styles.notCapable : styles.capable)}
/>
}
suffix={cond(
isComingSoonTagVisible && (
<Tag>
<DynamicT forKey="general.coming_soon" />
</Tag>
)
)}
/>
);
}
export default FeaturedQuotaItem;

View file

@ -1,6 +0,0 @@
@use '@/scss/underscore' as _;
.featuredQuotaList {
flex: 1;
padding-bottom: _.unit(8);
}

View file

@ -1,63 +0,0 @@
import { ReservedPlanId } from '@logto/schemas';
import { useMemo } from 'react';
import PlanQuotaList from '@/components/PlanQuotaList';
import { comingSoonQuotaKeys } from '@/consts/plan-quotas';
import { quotaItemAddOnPhrasesMap } from '@/consts/quota-item-phrases';
import {
type SubscriptionPlanQuotaEntries,
type SubscriptionPlan,
type SubscriptionPlanQuota,
} from '@/types/subscriptions';
import FeaturedQuotaItem from './FeaturedQuotaItem';
import * as styles from './index.module.scss';
const featuredQuotaKeys = new Set<keyof SubscriptionPlanQuota>([
'mauLimit',
'machineToMachineLimit',
'thirdPartyApplicationsLimit',
'rolesLimit',
'scopesPerRoleLimit',
'mfaEnabled',
'ssoEnabled',
'organizationsEnabled',
'auditLogsRetentionDays',
]);
type Props = {
plan: SubscriptionPlan;
};
function FeaturedPlanQuotaList({ plan }: Props) {
const { id: planId, quota } = plan;
const featuredEntries = useMemo(
() =>
// eslint-disable-next-line no-restricted-syntax
(Object.entries(quota) as SubscriptionPlanQuotaEntries).filter(([key]) =>
featuredQuotaKeys.has(key)
),
[quota]
);
return (
<PlanQuotaList
className={styles.featuredQuotaList}
entries={featuredEntries}
itemRenderer={(quotaKey, quotaValue) => (
<FeaturedQuotaItem
key={quotaKey}
quotaKey={quotaKey}
quotaValue={quotaValue}
isAddOnQuota={
planId !== ReservedPlanId.Free && Boolean(quotaItemAddOnPhrasesMap[quotaKey])
}
isComingSoonTagVisible={comingSoonQuotaKeys.includes(quotaKey)}
/>
)}
/>
);
}
export default FeaturedPlanQuotaList;

View file

@ -14,7 +14,7 @@ import DynamicT from '@/ds-components/DynamicT';
import TextLink from '@/ds-components/TextLink';
import { type SubscriptionPlan } from '@/types/subscriptions';
import FeaturedPlanQuotaList from './FeaturedPlanQuotaList';
import FeaturedPlanContent from './FeaturedPlanContent';
import * as styles from './index.module.scss';
type Props = {
@ -59,7 +59,7 @@ function PlanCardItem({ plan, onSelect }: Props) {
</div>
</div>
<div className={styles.content}>
<FeaturedPlanQuotaList plan={plan} />
<FeaturedPlanContent planId={planId} />
{isFreePlan && isFreeTenantExceeded && (
<div className={classNames(styles.tip, styles.exceedFreeTenantsTip)}>
{t('free_tenants_limit', { count: maxFreeTenantLimit })}

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Bis zu {{count, number}} MAU',
pro_plan: 'Unbegrenzte MAU',
},
m2m: {
free_plan: '{{count, number}} Maschine-zu-Maschine',
pro_plan: 'Zusätzliche Maschine-zu-Maschine',
},
third_party_apps: 'IdP für Drittanbieteranwendungen',
mfa: 'Multi-Faktor-Authentifizierung',
sso: 'Unternehmens-SSO',
role_and_permissions: {
free_plan: '{{roleCount, number}} Rolle und {{permissionCount, number}} Berechtigung pro Rolle',
pro_plan: 'Unbegrenzte Rollen und Berechtigungen pro Rolle',
},
organizations: 'Organisationen',
audit_logs: 'Audit-Logs Speicherung: {{count, number}} Tage',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'Sie haben Ihr {{item}}-Quotenlimit überschritten. Logto wird Gebühren für die Nutzung über Ihr Quotenlimit hinaus hinzufügen. Die Abrechnung beginnt am Tag der Veröffentlichung des neuen Add-On-Preisdesigns. <a>Mehr erfahren</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Up to {{count, number}} MAU',
pro_plan: 'Unlimited MAU',
},
m2m: {
free_plan: '{{count, number}} machine-to-machine',
pro_plan: 'Additional machine-to-machine',
},
third_party_apps: 'IdP for third-party applications',
mfa: 'Multi-factor authentication',
sso: 'Enterprise SSO',
role_and_permissions: {
free_plan: '{{roleCount, number}} role and {{permissionCount, number}} permission per role',
pro_plan: 'Unlimited roles and permissions per role',
},
organizations: 'Organizations',
audit_logs: 'Audit logs retention: {{count, number}} days',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'You have surpassed your {{item}} quota limit. Logto will add charges for the usage beyond your quota limit. Charging will commence on the day the new add-on pricing design is released. <a>Learn more</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Hasta {{count, number}} MAU',
pro_plan: 'MAU ilimitados',
},
m2m: {
free_plan: '{{count, number}} de máquina a máquina',
pro_plan: 'Máquina a máquina adicional',
},
third_party_apps: 'IdP para aplicaciones de terceros',
mfa: 'Autenticación multifactor',
sso: 'SSO empresarial',
role_and_permissions: {
free_plan: '{{roleCount, number}} rol y {{permissionCount, number}} permiso por rol',
pro_plan: 'Roles y permisos ilimitados por rol',
},
organizations: 'Organizaciones',
audit_logs: 'Retención de registros de auditoría: {{count, number}} días',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'Has superado tu límite de cuota de {{item}}. Logto agregará cargos por el uso más allá de tu límite de cuota. La facturación comenzará el día en que se lance el nuevo diseño de precios del complemento. <a>Más información</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: "Jusqu'à {{count, number}} MAU",
pro_plan: 'MAU illimités',
},
m2m: {
free_plan: '{{count, number}} machine à machine',
pro_plan: 'Machine à machine supplémentaire',
},
third_party_apps: 'IdP pour les applications tierces',
mfa: 'Authentification multi-facteurs',
sso: 'SSO Entreprise',
role_and_permissions: {
free_plan: '{{roleCount, number}} rôle et {{permissionCount, number}} permission par rôle',
pro_plan: 'Rôles et permissions illimités par rôle',
},
organizations: 'Organisations',
audit_logs: "Conservation des journaux d'audit: {{count, number}} jours",
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
"Vous avez dépassé votre limite de quota {{item}}. Logto ajoutera des frais pour l'utilisation au-delà de votre limite de quota. La facturation commencera le jour de la publication du nouveau design tarifaire de l'extension. <a>En savoir plus</a>",
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Fino a {{count, number}} MAU',
pro_plan: 'MAU illimitati',
},
m2m: {
free_plan: '{{count, number}} da macchina a macchina',
pro_plan: 'Macchina a macchina aggiuntiva',
},
third_party_apps: 'IdP per applicazioni di terze parti',
mfa: 'Autenticazione a più fattori',
sso: 'SSO aziendale',
role_and_permissions: {
free_plan: '{{roleCount, number}} ruolo e {{permissionCount, number}} permesso per ruolo',
pro_plan: 'Ruoli e permessi illimitati per ruolo',
},
organizations: 'Organizzazioni',
audit_logs: 'Conservazione dei log degli audit: {{count, number}} giorni',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
"Hai superato il limite di quota {{item}}. Logto aggiungerà addebiti per l'uso oltre il limite di quota. La fatturazione inizierà il giorno in cui verrà rilasciato il nuovo design dei prezzi dell'addon. <a>Ulteriori informazioni</a>",
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: '{{count, number}} MAUまで',
pro_plan: 'MAU無制限',
},
m2m: {
free_plan: '{{count, number}}機器間',
pro_plan: '追加の機器間',
},
third_party_apps: 'サードパーティアプリケーションのIdP',
mfa: 'マルチファクタ認証',
sso: '企業SSO',
role_and_permissions: {
free_plan: '{{roleCount, number}}ロールと{{permissionCount, number}}ロールごとの権限',
pro_plan: 'ロールごとの無制限の役割と権限',
},
organizations: '組織',
audit_logs: '監査ログ保持: {{count, number}} 日間',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'{{item}} のクォータ制限を超えています。Logto はクォータ制限を超える利用に対して料金を追加します。新しいアドオン価格設計がリリースされる日から請求が開始されます。 <a>詳細</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: '{{count, number}} MAU까지',
pro_plan: '무제한 MAU',
},
m2m: {
free_plan: '{{count, number}} 기기 간 통신',
pro_plan: '추가 기기 간 통신',
},
third_party_apps: '타사 응용 프로그램을위한 IdP',
mfa: '다중 인증',
sso: '기업 SSO',
role_and_permissions: {
free_plan: '{{roleCount, number}} 역할 및 {{permissionCount, number}} 역할당 권한',
pro_plan: '역할당 무제한 역할 및 권한',
},
organizations: '조직',
audit_logs: '감사 로그 보존: {{count, number}} 일',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'{{item}} 할당량 한도를 초과했습니다. Logto는 할당량을 초과하는 사용에 대한 요금을 추가합니다. 새로운 애드온 가격 디자인이 출시된 날부터 청구가 시작됩니다. <a>더 알아보기</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Do {{count, number}} MAU',
pro_plan: 'Nieograniczony MAU',
},
m2m: {
free_plan: '{{count, number}} urządzenia do urządzenia',
pro_plan: 'Dodatkowe urządzenie do urządzenia',
},
third_party_apps: 'IdP dla aplikacji innych firm',
mfa: 'Autoryzacja wieloskładnikowa',
sso: 'SSO dla przedsiębiorstw',
role_and_permissions: {
free_plan: '{{roleCount, number}} rola i {{permissionCount, number}} uprawnienie na rolę',
pro_plan: 'Nieograniczone role i uprawnienia na rolę',
},
organizations: 'Organizacje',
audit_logs: 'Przechowywanie dzienników audytu: {{count, number}} dni',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'Przekroczyłeś limit kwoty {{item}}. Logto doliczy opłaty za korzystanie poza limitem kwoty. Fakturowanie rozpocznie się w dniu wprowadzenia nowego projektu cenowego dodatku. <a>Dowiedz się więcej</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Até {{count, number}} MAU',
pro_plan: 'MAU ilimitados',
},
m2m: {
free_plan: '{{count, number}} de máquina para máquina',
pro_plan: 'Máquina para máquina adicional',
},
third_party_apps: 'IdP para aplicativos de terceiros',
mfa: 'Autenticação de vários fatores',
sso: 'SSO empresarial',
role_and_permissions: {
free_plan: '{{roleCount, number}} função e {{permissionCount, number}} permissão por função',
pro_plan: 'Funções e permissões ilimitadas por função',
},
organizations: 'Organizações',
audit_logs: 'Retenção de logs de auditoria: {{count, number}} dias',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'Você ultrapassou o limite de sua cota de {{item}}. O Logto adicionará cobranças pelo uso além do limite da cota. A cobrança começará no dia em que o novo design de preços do complemento for lançado. <a>Saiba mais</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'Até {{count, number}} MAU',
pro_plan: 'MAU ilimitados',
},
m2m: {
free_plan: '{{count, number}} de máquina para máquina',
pro_plan: 'Máquina para máquina adicional',
},
third_party_apps: 'IdP para aplicações de terceiros',
mfa: 'Autenticação de vários fatores',
sso: 'SSO empresarial',
role_and_permissions: {
free_plan: '{{roleCount, number}} função e {{permissionCount, number}} permissão por função',
pro_plan: 'Funções e permissões ilimitadas por função',
},
organizations: 'Organizações',
audit_logs: 'Retenção de logs de auditoria: {{count, number}} dias',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'Você ultrapassou o limite de sua cota de {{item}}. O Logto adicionará cobranças pelo uso além do limite da cota. A cobrança começará no dia em que o novo design de preços do complemento for lançado. <a>Saiba mais</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: 'До {{count, number}} MAU',
pro_plan: 'Неограниченные MAU',
},
m2m: {
free_plan: '{{count, number}} от устройства к устройству',
pro_plan: 'Дополнительное устройство к устройству',
},
third_party_apps: 'IdP для сторонних приложений',
mfa: 'Многофакторная аутентификация',
sso: 'Корпоративный SSO',
role_and_permissions: {
free_plan: '{{roleCount, number}} роль и {{permissionCount, number}} разрешение на роль',
pro_plan: 'Неограниченные роли и разрешения на роль',
},
organizations: 'Организации',
audit_logs: 'Хранение журналов аудита: {{count, number}} дней',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'Вы превысили лимит вашей квоты по {{item}}. Logto начнет взимать плату за использование сверх вашей квоты. Начисление начнется в день выпуска нового дизайна цен на дополнение. <a>Узнать больше</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: "{{count, number}} MAU'ya kadar",
pro_plan: 'Sınırsız MAU',
},
m2m: {
free_plan: '{{count, number}} makineye makineye',
pro_plan: 'Ek makineye makineye',
},
third_party_apps: 'Üçüncü taraf uygulamalar için IdP',
mfa: 'Çok faktörlü kimlik doğrulama',
sso: 'Kurumsal SSO',
role_and_permissions: {
free_plan: '{{roleCount, number}} rol ve {{permissionCount, number}} izin başına rol',
pro_plan: 'Rol başına sınırsız roller ve izinler',
},
organizations: 'Organizasyonlar',
audit_logs: 'Denetim günlükleri saklama: {{count, number}} gün',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'{{item}} kota sınırını aştınız. Logto, kota sınırınızın ötesindeki kullanım için ücret ekleyecektir. Yeni ek paket fiyatlandırma tasarımı gününüzde başlayacaktır. <a>Daha fazla bilgi</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: '最多{{count, number}} MAU',
pro_plan: '无限 MAU',
},
m2m: {
free_plan: '{{count, number}} 机器对机器',
pro_plan: '额外的机器对机器',
},
third_party_apps: '第三方应用的 IdP',
mfa: '多因素认证',
sso: '企业单点登录',
role_and_permissions: {
free_plan: '{{roleCount, number}} 角色和每个角色 {{permissionCount, number}} 权限',
pro_plan: '无限角色和每个角色权限',
},
organizations: '组织',
audit_logs: '审计日志保留:{{count, number}} 天',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'您已超过{{item}}配额限制。Logto将为超出配额限制的使用添加费用。计费将从新的附加定价设计发布当天开始。 <a>了解更多</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: '最多{{count, number}} MAU',
pro_plan: '無限 MAU',
},
m2m: {
free_plan: '{{count, number}} 機器對機器',
pro_plan: '額外的機器對機器',
},
third_party_apps: '第三方應用的 IdP',
mfa: '多因素認證',
sso: '企業單點登錄',
role_and_permissions: {
free_plan: '{{roleCount, number}} 角色和每個角色 {{permissionCount, number}} 權限',
pro_plan: '無限角色和每個角色權限',
},
organizations: '組織',
audit_logs: '審計日誌保留:{{count, number}} 天',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'您已超出{{item}}配額限制。Logto將為超出配額限制的使用添加費用。計費將從新的附加定價設計發布當天開始。 <a>了解更多</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);

View file

@ -0,0 +1,21 @@
const featured_plan_content = {
mau: {
free_plan: '最多{{count, number}} MAU',
pro_plan: '無限 MAU',
},
m2m: {
free_plan: '{{count, number}} 機器對機器',
pro_plan: '額外的機器對機器',
},
third_party_apps: '第三方應用的 IdP',
mfa: '多因素認證',
sso: '企業單點登錄',
role_and_permissions: {
free_plan: '{{roleCount, number}} 角色和每個角色 {{permissionCount, number}} 權限',
pro_plan: '無限角色和每個角色權限',
},
organizations: '組織',
audit_logs: '審計日誌保留:{{count, number}} 天',
};
export default Object.freeze(featured_plan_content);

View file

@ -1,3 +1,4 @@
import featured_plan_content from './featured-plan-content.js';
import paywall from './paywall.js';
const upsell = {
@ -38,6 +39,7 @@ const upsell = {
charge_notification_for_quota_limit:
'您已超出{{item}}額度限制。Logto將為超出額度限制的使用添加費用。計費將從新的附加價格設計發布當天開始。 <a>了解更多</a>',
paywall,
featured_plan_content,
};
export default Object.freeze(upsell);