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

feat(console,core,phrases): add quota guard for cloud collaboration in console (#5644)

This commit is contained in:
Charles Zhao 2024-04-08 10:32:05 +08:00 committed by GitHub
parent a6a32c57fb
commit cfeb98c06f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
75 changed files with 713 additions and 174 deletions

View file

@ -48,6 +48,6 @@
"access": "public"
},
"devDependencies": {
"@logto/cloud": "0.2.5-1807f9c"
"@logto/cloud": "0.2.5-ab8a489"
}
}

View file

@ -28,7 +28,7 @@
"@fontsource/roboto-mono": "^5.0.0",
"@jest/types": "^29.5.0",
"@logto/app-insights": "workspace:^1.4.0",
"@logto/cloud": "0.2.5-1807f9c",
"@logto/cloud": "0.2.5-ab8a489",
"@logto/connector-kit": "workspace:^2.1.0",
"@logto/core-kit": "workspace:^2.3.0",
"@logto/language-kit": "workspace:^1.1.0",

View file

@ -26,6 +26,7 @@ export const quotaItemPhrasesMap: Record<
mfaEnabled: 'mfa_enabled.name',
organizationsEnabled: 'organizations_enabled.name',
ssoEnabled: 'sso_enabled.name',
tenantMembersLimit: 'tenant_members_limit.name',
};
export const quotaItemUnlimitedPhrasesMap: Record<
@ -52,6 +53,7 @@ export const quotaItemUnlimitedPhrasesMap: Record<
mfaEnabled: 'mfa_enabled.unlimited',
organizationsEnabled: 'organizations_enabled.unlimited',
ssoEnabled: 'sso_enabled.unlimited',
tenantMembersLimit: 'tenant_members_limit.unlimited',
};
export const quotaItemLimitedPhrasesMap: Record<
@ -78,6 +80,7 @@ export const quotaItemLimitedPhrasesMap: Record<
mfaEnabled: 'mfa_enabled.limited',
organizationsEnabled: 'organizations_enabled.limited',
ssoEnabled: 'sso_enabled.limited',
tenantMembersLimit: 'tenant_members_limit.limited',
};
export const quotaItemNotEligiblePhrasesMap: Record<
@ -104,4 +107,5 @@ export const quotaItemNotEligiblePhrasesMap: Record<
mfaEnabled: 'mfa_enabled.not_eligible',
organizationsEnabled: 'organizations_enabled.not_eligible',
ssoEnabled: 'sso_enabled.not_eligible',
tenantMembersLimit: 'tenant_members_limit.not_eligible',
};

View file

@ -66,6 +66,7 @@ export const defaultSubscriptionPlan: SubscriptionPlan = {
ssoEnabled: true,
ticketSupportResponseTime: 48,
thirdPartyApplicationsLimit: null,
tenantMembersLimit: null,
},
};

View file

@ -119,13 +119,15 @@ function PlanComparisonTable() {
const orgPermissions = t('organizations.org_permissions');
const jitProvisioning = t('organizations.just_in_time_provisioning');
// Audit logs
const auditLogRetention = t('audit_logs.retention');
// Developers and platform
const webhooks = t('developers_and_platform.hooks');
const auditLogRetention = t('developers_and_platform.audit_logs_retention');
const freePlanLogRetention = t('days', { count: freePlanAuditLogsRetentionDays });
const paidPlanLogRetention = t('days', { count: proPlanAuditLogsRetentionDays });
// Webhooks
const webhooks = t('hooks.hooks');
const jwtClaims = t('developers_and_platform.jwt_claims');
const tenantMembers = t('developers_and_platform.tenant_members');
const tenantMembersLimit = t('included', { value: 3 });
const tenantMembersPrice = t('per_member', { value: 8 });
// Compliance and support
const community = t('support.community');
@ -228,15 +230,21 @@ function PlanComparisonTable() {
],
},
{
title: 'audit_logs.title',
title: 'developers_and_platform.title',
rows: [
{ name: webhooks, data: ['1', '10', contact] },
{ name: auditLogRetention, data: [freePlanLogRetention, paidPlanLogRetention, contact] },
{ name: jwtClaims, data: ['✓', '✓', '✓'] },
{
name: tenantMembers,
data: [
'1',
`${tenantMembersLimit}|${paidAddOnFeatureTip}|${tenantMembersPrice}`,
contact,
],
},
],
},
{
title: 'hooks.title',
rows: [{ name: webhooks, data: ['1', '10', contact] }],
},
{
title: 'support.title',
rows: [
@ -275,8 +283,9 @@ function PlanComparisonTable() {
<td className={styles.quotaKeyColumn}>
<TableDataWrapper isLeftAligned value={name} />
</td>
{data.map((value) => (
<td key={value}>
{data.map((value, index) => (
// eslint-disable-next-line react/no-array-index-key
<td key={`${title}-${name}-${index}`}>
<TableDataWrapper value={value} />
</td>
))}

View file

@ -0,0 +1,16 @@
@use '@/scss/underscore' as _;
.container {
display: flex;
align-items: center;
gap: _.unit(6);
padding: _.unit(6);
background-color: var(--color-info-container);
margin: 0 _.unit(-6) _.unit(-6);
.description {
flex: 1;
flex-shrink: 0;
font: var(--font-body-2);
}
}

View file

@ -0,0 +1,74 @@
import { ReservedPlanId } from '@logto/schemas';
import { useContext } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import ContactUsPhraseLink from '@/components/ContactUsPhraseLink';
import QuotaGuardFooter from '@/components/QuotaGuardFooter';
import { contactEmailLink } from '@/consts';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import Button, { LinkButton } from '@/ds-components/Button';
import useTenantMembersUsage from '../../hooks';
import * as styles from './index.module.scss';
type Props = {
newInvitationCount?: number;
isLoading: boolean;
onSubmit: () => void;
};
function Footer({ newInvitationCount = 0, isLoading, onSubmit }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.upsell.paywall' });
const { currentPlan } = useContext(SubscriptionDataContext);
const { id: planId, quota } = currentPlan;
const { hasTenantMembersReachedLimit, limit, usage } = useTenantMembersUsage();
if (planId === ReservedPlanId.Free && hasTenantMembersReachedLimit) {
return (
<QuotaGuardFooter>
<Trans
components={{
a: <ContactUsPhraseLink />,
}}
>
{t('tenant_members')}
</Trans>
</QuotaGuardFooter>
);
}
if (
planId === ReservedPlanId.Development &&
(hasTenantMembersReachedLimit || usage + newInvitationCount > limit)
) {
// Display a custom "Contact us" footer instead of asking for upgrade
return (
<div className={styles.container}>
<div className={styles.description}>
{t('tenant_members_dev_plan', { limit: quota.tenantMembersLimit })}
</div>
<LinkButton
size="large"
type="primary"
title="general.contact_us_action"
href={contactEmailLink}
/>
</div>
);
}
return (
<Button
size="large"
type="primary"
title="tenant_members.invite_members"
isLoading={isLoading}
onClick={onSubmit}
/>
);
}
export default Footer;

View file

@ -1,4 +1,4 @@
import { ReservedPlanId, TenantRole } from '@logto/schemas';
import { TenantRole } from '@logto/schemas';
import { useContext, useEffect, useMemo, useState } from 'react';
import { Controller, FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
@ -6,9 +6,7 @@ import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import Button from '@/ds-components/Button';
import FormField from '@/ds-components/FormField';
import ModalLayout from '@/ds-components/ModalLayout';
import Select, { type Option } from '@/ds-components/Select';
@ -18,6 +16,8 @@ import InviteEmailsInput from '../InviteEmailsInput';
import useEmailInputUtils from '../InviteEmailsInput/hooks';
import { type InviteMemberForm } from '../types';
import Footer from './Footer';
type Props = {
isOpen: boolean;
onClose: (isSuccessful?: boolean) => void;
@ -25,10 +25,7 @@ type Props = {
function InviteMemberModal({ isOpen, onClose }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.tenant_members' });
const { currentPlan } = useContext(SubscriptionDataContext);
const { currentTenantId } = useContext(TenantsContext);
// TODO: @charles update with actual quota guard later
const tenantMembersMaxLimit = currentPlan.id === ReservedPlanId.Free ? 1 : 3;
const [isLoading, setIsLoading] = useState(false);
const cloudApi = useAuthedCloudApi();
@ -44,8 +41,8 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
const {
control,
handleSubmit,
setError,
reset,
watch,
formState: { errors },
} = formMethods;
@ -66,22 +63,6 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
const onSubmit = handleSubmit(async ({ emails, role }) => {
setIsLoading(true);
try {
// Do not check seats for Pro plan for now
if (currentPlan.id === ReservedPlanId.Free || currentPlan.id === ReservedPlanId.Development) {
// Count the current tenant members
const members = await cloudApi.get(`/api/tenants/:tenantId/members`, {
params: { tenantId: currentTenantId },
});
// Check if it will exceed the tenant member limit
if (emails.length + members.length > tenantMembersMaxLimit) {
setError('emails', {
type: 'custom',
message: t('errors.max_member_limit', { limit: tenantMembersMaxLimit }),
});
return;
}
}
await Promise.all(
emails.map(async (email) =>
cloudApi.post('/api/tenants/:tenantId/invitations', {
@ -107,14 +88,14 @@ function InviteMemberModal({ isOpen, onClose }: Props) {
}}
>
<ModalLayout
size="large"
title="tenant_members.invite_modal.title"
subtitle="tenant_members.invite_modal.subtitle"
footer={
<Button
size="large"
type="primary"
title="tenant_members.invite_members"
<Footer
newInvitationCount={watch('emails').length}
isLoading={isLoading}
onClick={onSubmit}
onSubmit={onSubmit}
/>
}
onClose={onClose}

View file

@ -0,0 +1,66 @@
import { OrganizationInvitationStatus } from '@logto/schemas';
import { useContext, useMemo } from 'react';
import useSWR from 'swr';
import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api';
import { type TenantInvitationResponse, type TenantMemberResponse } from '@/cloud/types/router';
import { SubscriptionDataContext } from '@/contexts/SubscriptionDataProvider';
import { TenantsContext } from '@/contexts/TenantsProvider';
import { type RequestError } from '@/hooks/use-api';
import { hasReachedQuotaLimit, hasSurpassedQuotaLimit } from '@/utils/quota';
const useTenantMembersUsage = () => {
const { currentPlan } = useContext(SubscriptionDataContext);
const { currentTenantId } = useContext(TenantsContext);
const cloudApi = useAuthedCloudApi();
const { data: members } = useSWR<TenantMemberResponse[], RequestError>(
`api/tenants/:tenantId/members`,
async () =>
cloudApi.get('/api/tenants/:tenantId/members', { params: { tenantId: currentTenantId } })
);
const { data: invitations } = useSWR<TenantInvitationResponse[], RequestError>(
'api/tenants/:tenantId/invitations',
async () =>
cloudApi.get('/api/tenants/:tenantId/invitations', { params: { tenantId: currentTenantId } })
);
const pendingInvitations = useMemo(
() => invitations?.filter(({ status }) => status === OrganizationInvitationStatus.Pending),
[invitations]
);
const usage = useMemo(() => {
return (members?.length ?? 0) + (pendingInvitations?.length ?? 0);
}, [members?.length, pendingInvitations?.length]);
const hasTenantMembersReachedLimit = useMemo(
() =>
hasReachedQuotaLimit({
quotaKey: 'tenantMembersLimit',
plan: currentPlan,
usage,
}),
[currentPlan, usage]
);
const hasTenantMembersSurpassedLimit = useMemo(
() =>
hasSurpassedQuotaLimit({
quotaKey: 'tenantMembersLimit',
plan: currentPlan,
usage,
}),
[currentPlan, usage]
);
return {
hasTenantMembersReachedLimit,
hasTenantMembersSurpassedLimit,
usage,
limit: currentPlan.quota.tenantMembersLimit ?? Number.POSITIVE_INFINITY,
};
};
export default useTenantMembersUsage;

View file

@ -5,6 +5,10 @@
flex-direction: column;
gap: _.unit(4);
.chargeNotification {
margin: _.unit(4) 0 0;
}
.tabButtons {
display: flex;
align-items: center;

View file

@ -7,6 +7,7 @@ import InvitationIcon from '@/assets/icons/invitation.svg';
import MembersIcon from '@/assets/icons/members.svg';
import PlusIcon from '@/assets/icons/plus.svg';
import { useAuthedCloudApi } from '@/cloud/hooks/use-cloud-api';
import ChargeNotification from '@/components/ChargeNotification';
import { TenantSettingsTabs } from '@/consts';
import { TenantsContext } from '@/contexts/TenantsProvider';
import Button from '@/ds-components/Button';
@ -18,11 +19,13 @@ import NotFound from '@/pages/NotFound';
import Invitations from './Invitations';
import InviteMemberModal from './InviteMemberModal';
import Members from './Members';
import useTenantMembersUsage from './hooks';
import * as styles from './index.module.scss';
const invitationsRoute = 'invitations';
function TenantMembers() {
const { hasTenantMembersSurpassedLimit } = useTenantMembersUsage();
const { navigate, match } = useTenantPathname();
const [showInviteModal, setShowInviteModal] = useState(false);
const { canInviteMember } = useCurrentTenantScopes();
@ -41,6 +44,12 @@ function TenantMembers() {
return (
<div className={styles.container}>
<ChargeNotification
hasSurpassedLimit={hasTenantMembersSurpassedLimit}
quotaItemPhraseKey="tenant_member"
className={styles.chargeNotification}
checkedFlagKey="tenantMember"
/>
<div className={styles.tabButtons}>
<Button
className={classNames(styles.button, !isInvitationTab && styles.active)}

View file

@ -91,7 +91,7 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@logto/cloud": "0.2.5-1807f9c",
"@logto/cloud": "0.2.5-ab8a489",
"@silverhand/eslint-config": "5.0.0",
"@silverhand/ts-config": "5.0.0",
"@types/debug": "^4.1.7",

View file

@ -65,6 +65,7 @@ export const createQuotaLibrary = (
).length;
return { count };
},
tenantMembersLimit: notNumber, // Cloud Admin tenant feature, no limit for now
customDomainEnabled: notNumber,
mfaEnabled: notNumber,
organizationsEnabled: notNumber,

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'Enterprise SSO',
not_eligible: 'Entferne dein Enterprise SSO',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Maschine-zu-Maschine Rollen',
scopes_per_role: 'Berechtigungen pro Rolle',
},
audit_logs: {
title: 'Prüfprotokolle',
retention: 'Aufbewahrungsdauer',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: 'Organisation',
organizations: 'Organisationen',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2-Bericht',
hipaa_or_baa_report: 'HIPAA/BAA-Bericht',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Unbegrenzt',
contact: 'Kontakt',
monthly_price: '${{value, number}}/Monat',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} pro Monat / je',
extra_mao_price: 'Dann ${{value, number}} pro MAO',
per_month: '${{value, number}} pro Monat',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API-Ressource',
machine_to_machine: 'Machine-to-Machine-Anwendung',
tokens: '{{limit}}M Tokens',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -146,6 +146,13 @@ const quota_item = {
unlimited: 'Enterprise SSO',
not_eligible: 'Remove your Enterprise SSO',
},
tenant_members_limit: {
name: 'Tenant members',
limited: '{{count, number}} tenant member',
limited_other: '{{count, number}} tenant members',
unlimited: 'Unlimited tenant members',
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -45,14 +45,6 @@ const quota_table = {
machine_to_machine_roles: 'Machine-to-machine roles',
scopes_per_role: 'Permissions per role',
},
audit_logs: {
title: 'Audit logs',
retention: 'Retention',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: 'Organizations',
organizations: 'Organizations',
@ -72,6 +64,13 @@ const quota_table = {
soc2_report: 'SOC2 report',
hipaa_or_baa_report: 'HIPAA/BAA report',
},
developers_and_platform: {
title: 'Developers and platform',
hooks: 'Webhooks',
audit_logs_retention: 'Audit logs retention',
jwt_claims: 'JWT claims',
tenant_members: 'Tenant members',
},
unlimited: 'Unlimited',
contact: 'Contact',
monthly_price: '${{value, number}}/mo',
@ -100,6 +99,7 @@ const quota_table = {
per_month_each: '${{value, number}} per mo / ea',
extra_mao_price: 'Then ${{value, number}} per MAO',
per_month: '${{value, number}} per mo',
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,7 @@ const upsell = {
api_resource: 'API resource',
machine_to_machine: 'machine-to-machine application',
tokens: '{{limit}}M tokens',
tenant_member: 'tenant member',
},
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>',

View file

@ -56,6 +56,10 @@ const paywall = {
'Unlock Logto as IdP for third-party apps by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'SSO empresarial',
not_eligible: 'Elimine su SSO empresarial',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Roles de máquina a máquina',
scopes_per_role: 'Permisos por rol',
},
audit_logs: {
title: 'Registros de auditoría',
retention: 'Retención',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: 'Organización',
organizations: 'Organizaciones',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Informe SOC2 (Próximamente)',
hipaa_or_baa_report: 'Informe HIPAA/BAA (Próximamente)',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Ilimitado',
contact: 'Contacto',
monthly_price: '${{value, number}}/mes',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} por mes / cada uno',
extra_mao_price: 'Luego ${{value, number}} por MAO',
per_month: '${{value, number}} por mes',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'Recurso de API',
machine_to_machine: 'aplicación de máquina a máquina',
tokens: '{{limit}}M tokens',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'SSO Entreprise',
not_eligible: 'Supprimez votre SSO Entreprise',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Rôles machine-à-machine',
scopes_per_role: 'Autorisations par rôle',
},
audit_logs: {
title: "Journaux d'audit",
retention: 'Conservation',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: 'Organisation',
organizations: 'Organisations',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Rapport SOC2',
hipaa_or_baa_report: 'Rapport HIPAA/BAA',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Illimité',
contact: 'Contact',
monthly_price: '${{value, number}}/mois',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} par mois / chacun',
extra_mao_price: 'Ensuite ${{value, number}} par MAO',
per_month: '${{value, number}} par mois',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'Ressource API',
machine_to_machine: 'application machine à machine',
tokens: '{{limit}}M jetons',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>",

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'SSO aziendale',
not_eligible: 'Rimuovi il tuo SSO aziendale',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Ruoli machine-to-machine',
scopes_per_role: 'Permessi per ruolo',
},
audit_logs: {
title: 'Log di audit',
retention: 'Conservazione',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: 'Organizzazione',
organizations: 'Organizzazioni',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Rapporto SOC2',
hipaa_or_baa_report: 'Rapporto HIPAA/BAA',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Illimitato',
contact: 'Contatta',
monthly_price: '${{value, number}}/mese',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} al mese / ognuno',
extra_mao_price: 'Quindi ${{value, number}} per MAO',
per_month: '${{value, number}} al mese',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'Risorsa API',
machine_to_machine: 'applicazione macchina-macchina',
tokens: '{{limit}}M token',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>",

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'エンタープライズSSO',
not_eligible: 'エンタープライズSSOを削除してください',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'マシン対マシンロール',
scopes_per_role: 'ロールごとの権限',
},
audit_logs: {
title: '監査ログ',
retention: '保持期間',
},
hooks: {
title: 'ウェブフック',
hooks: 'ウェブフック',
},
organizations: {
title: '組織',
organizations: '組織',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2レポート',
hipaa_or_baa_report: 'HIPAA/BAAレポート',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: '無制限',
contact: 'お問い合わせ',
monthly_price: '${{value, number}}/mo',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '各${{value, number}} / 月ごと',
extra_mao_price: 'その後、MAOごとに${{value, number}}',
per_month: '${{value, number}} / 月ごと',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API リソース',
machine_to_machine: 'マシン対マシンアプリケーション',
tokens: '{{limit}}M トークン',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
charge_notification_for_quota_limit:
'{{item}} のクォータ制限を超えています。Logto はクォータ制限を超える利用に対して料金を追加します。新しいアドオン価格設計がリリースされる日から請求が開始されます。 <a>詳細</a>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: '기업 SSO',
not_eligible: '기업 SSO를 제거하십시오',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: '머신 투 머신 역할',
scopes_per_role: '역할 당 권한',
},
audit_logs: {
title: '감사 로그',
retention: '보존 기간',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: '조직',
organizations: '조직',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2 보고서',
hipaa_or_baa_report: 'HIPAA/BAA 보고서',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: '무제한',
contact: '문의',
monthly_price: '${{value, number}}/월',
@ -101,6 +105,8 @@ const quota_table = {
per_month_each: '월당 ${{value, number}} / 각각',
extra_mao_price: '이후 MAO당 ${{value, number}}',
per_month: '월당 ${{value, number}}',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API 리소스',
machine_to_machine: '머신 투 머신 애플리케이션',
tokens: '{{limit}}M 토큰',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
charge_notification_for_quota_limit:
'{{item}} 할당량 한도를 초과했습니다. Logto는 할당량을 초과하는 사용에 대한 요금을 추가합니다. 새로운 애드온 가격 디자인이 출시된 날부터 청구가 시작됩니다. <a>더 알아보기</a>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'SSO przedsiębiorstwa',
not_eligible: 'Usuń swoje SSO przedsiębiorstwa',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Role maszyna-maszyna',
scopes_per_role: 'Uprawnienia na rolę',
},
audit_logs: {
title: 'Logi audytu',
retention: 'Okres przechowywania',
},
hooks: {
title: 'Webhooki',
hooks: 'Webhooki',
},
organizations: {
title: 'Organizacja',
organizations: 'Organizacje',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Raport SOC2',
hipaa_or_baa_report: 'Raport HIPAA/BAA',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Nieograniczone',
contact: 'Kontakt',
monthly_price: '${{value, number}}/mies.',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} za miesiąc / każdy',
extra_mao_price: 'Następnie ${{value, number}} za MAO',
per_month: '${{value, number}} za miesiąc',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'Zasób API',
machine_to_machine: 'aplikacja od maszyny do maszyny',
tokens: '{{limit}}M tokenów',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'SSO Empresarial',
not_eligible: 'Remova seu Enterprise SSO',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Funções de máquina-a-máquina',
scopes_per_role: 'Permissões por função',
},
audit_logs: {
title: 'Registros de auditoria',
retention: 'Retenção',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: 'Organização',
organizations: 'Organizações',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Relatório SOC2',
hipaa_or_baa_report: 'Relatório HIPAA/BAA',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Ilimitado',
contact: 'Contato',
monthly_price: '${ { value, number } }/mês',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} por mês / cada',
extra_mao_price: 'Então ${{value, number}} por MAO',
per_month: '${{value, number}} por mês',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'Recurso de API',
machine_to_machine: 'aplicação máquina a máquina',
tokens: '{{limit}}M tokens',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'SSO Empresarial',
not_eligible: 'Remover o teu SSO Empresarial',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Funções de máquina para máquina',
scopes_per_role: 'Permissões por função',
},
audit_logs: {
title: 'Registos de auditoria',
retention: 'Retenção',
},
hooks: {
title: 'Hooks',
hooks: 'Hooks',
},
organizations: {
title: 'Organização',
organizations: 'Organizações',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Relatório SOC2',
hipaa_or_baa_report: 'Relatório HIPAA/BAA',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Ilimitado',
contact: 'Contactar',
monthly_price: '${{value, number}}/mês',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} por mês / cada um',
extra_mao_price: 'Depois ${{value, number}} por MAO',
per_month: '${{value, number}} por mês',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'Recurso de API',
machine_to_machine: 'aplicação máquina a máquina',
tokens: '{{limit}}M tokens',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'Единый вход для предприятий',
not_eligible: 'Удалите свой Единый вход для предприятий',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Роли машины-машины',
scopes_per_role: 'Разрешения на роль',
},
audit_logs: {
title: 'Аудит журналов',
retention: 'Сохранение',
},
hooks: {
title: 'Вебхуки',
hooks: 'Вебхуки',
},
organizations: {
title: 'Организация',
organizations: 'Организации',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'Отчет SOC2',
hipaa_or_baa_report: 'Отчет HIPAA/BAA',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Неограниченно',
contact: 'Связаться',
monthly_price: '${{value, number}}/мес.',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: '${{value, number}} в месяц / за каждый',
extra_mao_price: 'Затем ${{value, number}} за MAO',
per_month: '${{value, number}} в месяц',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API-ресурс',
machine_to_machine: 'приложение "машина-машина"',
tokens: '{{limit}}M токенов',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
charge_notification_for_quota_limit:
'Вы превысили лимит вашей квоты по {{item}}. Logto начнет взимать плату за использование сверх вашей квоты. Начисление начнется в день выпуска нового дизайна цен на дополнение. <a>Узнать больше</a>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: 'Kurumsal SSO',
not_eligible: "Kurumsal SSO'nuzu kaldırın",
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: 'Makine-makine rolleri',
scopes_per_role: 'Rol başına izinler',
},
audit_logs: {
title: 'Denetim Günlükleri',
retention: 'Saklama',
},
hooks: {
title: 'Web Kancaları',
hooks: 'Web Kancaları',
},
organizations: {
title: 'Organizasyon',
organizations: 'Organizasyonlar',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2 raporu',
hipaa_or_baa_report: 'HIPAA/BAA raporu',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: 'Sınırsız',
contact: 'İletişim',
monthly_price: '${{value, number}}/ay',
@ -102,6 +106,8 @@ const quota_table = {
per_month_each: 'Aylık ${{value, number}} / her biri',
extra_mao_price: 'Sonra MAO başına ${{value, number}}',
per_month: 'Aylık ${{value, number}}',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API kaynağı',
machine_to_machine: 'makine-makine uygulaması',
tokens: '{{limit}}M jeton',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
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>',

View file

@ -58,6 +58,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: '企业SSO',
not_eligible: '移除你的 企业SSO',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: '机器对机器角色',
scopes_per_role: '每角色权限',
},
audit_logs: {
title: '审计日志',
retention: '保留期限',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: '组织',
organizations: '组织',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2报告',
hipaa_or_baa_report: 'HIPAA/BAA报告',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: '无限制',
contact: '联系',
monthly_price: '${{value, number}} / 月',
@ -99,6 +103,8 @@ const quota_table = {
per_month_each: '每月 ${{value, number}} / 每个',
extra_mao_price: '然后每 MAO ${{value, number}}',
per_month: '每月 ${{value, number}}',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API 资源',
machine_to_machine: '机器对机器应用',
tokens: '{{limit}}M 令牌',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
charge_notification_for_quota_limit:
'您已超过{{item}}配额限制。Logto将为超出配额限制的使用添加费用。计费将从新的附加定价设计发布当天开始。 <a>了解更多</a>',

View file

@ -57,6 +57,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: '企業SSO',
not_eligible: '移除您的企業單一登入',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: '機器對機器角色',
scopes_per_role: '每角色權限',
},
audit_logs: {
title: '審核日誌',
retention: '保留期限',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: '組織',
organizations: '組織',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2報告',
hipaa_or_baa_report: 'HIPAA/BAA報告',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: '無限制',
contact: '聯絡',
monthly_price: '${{value, number}}/月',
@ -99,6 +103,8 @@ const quota_table = {
per_month_each: '每月 ${{value, number}} / 每個',
extra_mao_price: '然後每 MAO ${{value, number}}',
per_month: '每月 ${{value, number}}',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API 資源',
machine_to_machine: '機器對機器應用',
tokens: '{{limit}}M 令牌',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
charge_notification_for_quota_limit:
'您已超出{{item}}配額限制。Logto將為超出配額限制的使用添加費用。計費將從新的附加定價設計發布當天開始。 <a>了解更多</a>',

View file

@ -57,6 +57,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -151,6 +151,18 @@ const quota_item = {
unlimited: '企業單一登錄',
not_eligible: '移除你的 企業單一登錄',
},
tenant_members_limit: {
/** UNTRANSLATED */
name: 'Tenant members',
/** UNTRANSLATED */
limited: '{{count, number}} tenant member',
/** UNTRANSLATED */
limited_other: '{{count, number}} tenant members',
/** UNTRANSLATED */
unlimited: 'Unlimited tenant members',
/** UNTRANSLATED */
not_eligible: 'Remove your tenant members',
},
};
export default Object.freeze(quota_item);

View file

@ -46,14 +46,6 @@ const quota_table = {
machine_to_machine_roles: '機器對機器角色',
scopes_per_role: '每角色權限',
},
audit_logs: {
title: '稽核日誌',
retention: '保留期限',
},
hooks: {
title: 'Webhooks',
hooks: 'Webhooks',
},
organizations: {
title: '組織',
organizations: '組織',
@ -73,6 +65,18 @@ const quota_table = {
soc2_report: 'SOC2 報告',
hipaa_or_baa_report: 'HIPAA/BAA 報告',
},
developers_and_platform: {
/** UNTRANSLATED */
title: 'Developers and platform',
/** UNTRANSLATED */
hooks: 'Webhooks',
/** UNTRANSLATED */
audit_logs_retention: 'Audit logs retention',
/** UNTRANSLATED */
jwt_claims: 'JWT claims',
/** UNTRANSLATED */
tenant_members: 'Tenant members',
},
unlimited: '無限制',
contact: '聯絡',
monthly_price: '${{value, number}}/月',
@ -99,6 +103,8 @@ const quota_table = {
per_month_each: '每月 ${{value, number}} / 每個',
extra_mao_price: '然後每 MAO ${{value, number}}',
per_month: '每月 ${{value, number}}',
/** UNTRANSLATED */
per_member: 'Then ${{value, number}} per member',
};
export default Object.freeze(quota_table);

View file

@ -35,6 +35,8 @@ const upsell = {
api_resource: 'API 資源',
machine_to_machine: '機器對機器應用',
tokens: '{{limit}}M 令牌',
/** UNTRANSLATED */
tenant_member: 'tenant member',
},
charge_notification_for_quota_limit:
'您已超出{{item}}額度限制。Logto將為超出額度限制的使用添加費用。計費將從新的附加價格設計發布當天開始。 <a>了解更多</a>',

View file

@ -57,6 +57,12 @@ const paywall = {
/** UNTRANSLATED */
sso_connectors:
'Unlock enterprise sso by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members:
'Unlock collaboration feature by upgrading to a paid plan. For any assistance, feel free to <a>contact us</a>.',
/** UNTRANSLATED */
tenant_members_dev_plan:
"You've reached your {{limit}}-member limit. Release a member or revoke a pending invitation to add someone new. Need more seats? Feel free to contact us.",
};
export default Object.freeze(paywall);

View file

@ -105,6 +105,7 @@ export const adminConsoleDataGuard = z.object({
token: z.boolean().optional(),
apiResource: z.boolean().optional(),
machineToMachineApp: z.boolean().optional(),
tenantMember: z.boolean().optional(),
})
.optional(),
});

19
pnpm-lock.yaml generated
View file

@ -1235,8 +1235,8 @@ importers:
version: 3.22.4
devDependencies:
'@logto/cloud':
specifier: 0.2.5-1807f9c
version: 0.2.5-1807f9c(zod@3.22.4)
specifier: 0.2.5-ab8a489
version: 0.2.5-ab8a489(zod@3.22.4)
'@rollup/plugin-commonjs':
specifier: ^25.0.0
version: 25.0.7(rollup@4.12.0)
@ -2715,8 +2715,8 @@ importers:
specifier: workspace:^1.4.0
version: link:../app-insights
'@logto/cloud':
specifier: 0.2.5-1807f9c
version: 0.2.5-1807f9c(zod@3.22.4)
specifier: 0.2.5-ab8a489
version: 0.2.5-ab8a489(zod@3.22.4)
'@logto/connector-kit':
specifier: workspace:^2.1.0
version: link:../toolkit/connector-kit
@ -3202,8 +3202,8 @@ importers:
version: 3.22.4
devDependencies:
'@logto/cloud':
specifier: 0.2.5-1807f9c
version: 0.2.5-1807f9c(zod@3.22.4)
specifier: 0.2.5-ab8a489
version: 0.2.5-ab8a489(zod@3.22.4)
'@silverhand/eslint-config':
specifier: 5.0.0
version: 5.0.0(eslint@8.44.0)(prettier@3.0.0)(typescript@5.3.3)
@ -7644,8 +7644,8 @@ packages:
jose: 5.2.2
dev: true
/@logto/cloud@0.2.5-1807f9c(zod@3.22.4):
resolution: {integrity: sha512-npHrIjd7l90rCEx4G6RHR0Xu4d7X1JPeGjGpfyLjPttx4MTMHHOZS/8GtsXDdrFKRioZUsAymvNfjhNKq/XoQg==}
/@logto/cloud@0.2.5-ab8a489(zod@3.22.4):
resolution: {integrity: sha512-nUD1n2CDe/nu6x4cOhXfJ5VyKKDqkKv+a/u9zSfbIMxIF0nShybd2LiCYJDO0SPuMqLnmlYFg+79KrdPCNvjIQ==}
engines: {node: ^20.9.0}
dependencies:
'@silverhand/essentials': 2.9.0
@ -17988,9 +17988,6 @@ packages:
resolution: {integrity: sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==}
engines: {node: '>= 12.0.0'}
hasBin: true
peerDependenciesMeta:
'@parcel/core':
optional: true
dependencies:
'@parcel/config-default': 2.9.3(@parcel/core@2.9.3)(postcss@8.4.31)
'@parcel/core': 2.9.3