0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(console,phrases): add organizations paywall (#4939)

This commit is contained in:
Charles Zhao 2023-11-22 14:40:01 +08:00 committed by GitHub
parent 0e334ecdb1
commit fbf6d89d9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 249 additions and 153 deletions

View file

@ -49,6 +49,6 @@
"access": "public" "access": "public"
}, },
"devDependencies": { "devDependencies": {
"@logto/cloud": "0.2.5-d434baa" "@logto/cloud": "0.2.5-9257c0d"
} }
} }

View file

@ -26,7 +26,7 @@
"@fontsource/roboto-mono": "^5.0.0", "@fontsource/roboto-mono": "^5.0.0",
"@jest/types": "^29.5.0", "@jest/types": "^29.5.0",
"@logto/app-insights": "workspace:^1.3.1", "@logto/app-insights": "workspace:^1.3.1",
"@logto/cloud": "0.2.5-d434baa", "@logto/cloud": "0.2.5-9257c0d",
"@logto/connector-kit": "workspace:^2.0.0", "@logto/connector-kit": "workspace:^2.0.0",
"@logto/core-kit": "workspace:^2.1.2", "@logto/core-kit": "workspace:^2.1.2",
"@logto/language-kit": "workspace:^1.0.0", "@logto/language-kit": "workspace:^1.0.0",

View file

@ -26,7 +26,7 @@ const featuredQuotaKeys: Array<keyof SubscriptionPlanQuota> = [
'scopesPerRoleLimit', 'scopesPerRoleLimit',
'mfaEnabled', 'mfaEnabled',
'ssoEnabled', 'ssoEnabled',
'organizationEnabled', 'organizationsEnabled',
'auditLogsRetentionDays', 'auditLogsRetentionDays',
]; ];

View file

@ -77,12 +77,6 @@ export const ticketSupportResponseTimeMap: Record<string, number | undefined> =
[ReservedPlanId.Pro]: 48, [ReservedPlanId.Pro]: 48,
}; };
export const organizationEnabledMap: EnabledFeatureMap = {
[ReservedPlanId.Free]: false,
[ReservedPlanId.Hobby]: true,
[ReservedPlanId.Pro]: true,
};
export const ssoEnabledMap: EnabledFeatureMap = { export const ssoEnabledMap: EnabledFeatureMap = {
[ReservedPlanId.Free]: false, [ReservedPlanId.Free]: false,
[ReservedPlanId.Hobby]: true, [ReservedPlanId.Hobby]: true,
@ -123,7 +117,7 @@ const enterprisePlanTable: SubscriptionPlanTable = {
hooksLimit: undefined, hooksLimit: undefined,
communitySupportEnabled: true, communitySupportEnabled: true,
ticketSupportResponseTime: undefined, ticketSupportResponseTime: undefined,
organizationEnabled: true, organizationsEnabled: true,
ssoEnabled: true, ssoEnabled: true,
}; };
@ -163,7 +157,7 @@ export const planTableGroupKeyMap: SubscriptionPlanTableGroupKeyMap = Object.fre
'rolesLimit', 'rolesLimit',
'scopesPerRoleLimit', 'scopesPerRoleLimit',
], ],
[SubscriptionPlanTableGroupKey.organization]: ['organizationEnabled'], [SubscriptionPlanTableGroupKey.organizations]: ['organizationsEnabled'],
[SubscriptionPlanTableGroupKey.auditLogs]: ['auditLogsRetentionDays'], [SubscriptionPlanTableGroupKey.auditLogs]: ['auditLogsRetentionDays'],
[SubscriptionPlanTableGroupKey.hooks]: ['hooksLimit'], [SubscriptionPlanTableGroupKey.hooks]: ['hooksLimit'],
[SubscriptionPlanTableGroupKey.support]: ['communitySupportEnabled', 'ticketSupportResponseTime'], [SubscriptionPlanTableGroupKey.support]: ['communitySupportEnabled', 'ticketSupportResponseTime'],

View file

@ -22,7 +22,7 @@ export const quotaItemPhrasesMap: Record<
communitySupportEnabled: 'community_support_enabled.name', communitySupportEnabled: 'community_support_enabled.name',
ticketSupportResponseTime: 'customer_ticket_support.name', ticketSupportResponseTime: 'customer_ticket_support.name',
mfaEnabled: 'mfa_enabled.name', mfaEnabled: 'mfa_enabled.name',
organizationEnabled: 'organization_enabled.name', organizationsEnabled: 'organizations_enabled.name',
ssoEnabled: 'sso_enabled.name', ssoEnabled: 'sso_enabled.name',
}; };
@ -46,7 +46,7 @@ export const quotaItemUnlimitedPhrasesMap: Record<
communitySupportEnabled: 'community_support_enabled.unlimited', communitySupportEnabled: 'community_support_enabled.unlimited',
ticketSupportResponseTime: 'customer_ticket_support.unlimited', ticketSupportResponseTime: 'customer_ticket_support.unlimited',
mfaEnabled: 'mfa_enabled.unlimited', mfaEnabled: 'mfa_enabled.unlimited',
organizationEnabled: 'organization_enabled.unlimited', organizationsEnabled: 'organizations_enabled.unlimited',
ssoEnabled: 'sso_enabled.unlimited', ssoEnabled: 'sso_enabled.unlimited',
}; };
@ -70,7 +70,7 @@ export const quotaItemLimitedPhrasesMap: Record<
communitySupportEnabled: 'community_support_enabled.limited', communitySupportEnabled: 'community_support_enabled.limited',
ticketSupportResponseTime: 'customer_ticket_support.limited', ticketSupportResponseTime: 'customer_ticket_support.limited',
mfaEnabled: 'mfa_enabled.limited', mfaEnabled: 'mfa_enabled.limited',
organizationEnabled: 'organization_enabled.limited', organizationsEnabled: 'organizations_enabled.limited',
ssoEnabled: 'sso_enabled.limited', ssoEnabled: 'sso_enabled.limited',
}; };
@ -94,6 +94,6 @@ export const quotaItemNotEligiblePhrasesMap: Record<
communitySupportEnabled: 'community_support_enabled.not_eligible', communitySupportEnabled: 'community_support_enabled.not_eligible',
ticketSupportResponseTime: 'customer_ticket_support.not_eligible', ticketSupportResponseTime: 'customer_ticket_support.not_eligible',
mfaEnabled: 'mfa_enabled.not_eligible', mfaEnabled: 'mfa_enabled.not_eligible',
organizationEnabled: 'organization_enabled.not_eligible', organizationsEnabled: 'organizations_enabled.not_eligible',
ssoEnabled: 'sso_enabled.not_eligible', ssoEnabled: 'sso_enabled.not_eligible',
}; };

View file

@ -4,17 +4,18 @@ import { useTranslation } from 'react-i18next';
import Plus from '@/assets/icons/plus.svg'; import Plus from '@/assets/icons/plus.svg';
import OrganizationEmptyDark from '@/assets/images/organization-empty-dark.svg'; import OrganizationEmptyDark from '@/assets/images/organization-empty-dark.svg';
import OrganizationEmpty from '@/assets/images/organization-empty.svg'; import OrganizationEmpty from '@/assets/images/organization-empty.svg';
import Button from '@/ds-components/Button'; import Button, { type Props as ButtonProps } from '@/ds-components/Button';
import useConfigs from '@/hooks/use-configs'; import useConfigs from '@/hooks/use-configs';
import useTheme from '@/hooks/use-theme'; import useTheme from '@/hooks/use-theme';
import * as styles from './index.module.scss'; import * as styles from './index.module.scss';
type Props = { type Props = {
onCreate: () => void; /** Override the default button properties in the placeholder */
buttonProps?: ButtonProps;
}; };
function EmptyDataPlaceholder({ onCreate }: Props) { function EmptyDataPlaceholder({ buttonProps }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.organizations' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console.organizations' });
const { configs } = useConfigs(); const { configs } = useConfigs();
const theme = useTheme(); const theme = useTheme();
@ -34,7 +35,7 @@ function EmptyDataPlaceholder({ onCreate }: Props) {
title={ title={
isInitialSetup ? 'organizations.setup_organization' : 'organizations.create_organization' isInitialSetup ? 'organizations.setup_organization' : 'organizations.create_organization'
} }
onClick={onCreate} {...buttonProps}
/> />
</div> </div>
); );

View file

@ -51,7 +51,11 @@ function OrganizationsTable({ isLoading, onCreate }: Props) {
<Table <Table
className={pageLayout.table} className={pageLayout.table}
isLoading={isTableLoading} isLoading={isTableLoading}
placeholder={<EmptyDataPlaceholder onCreate={onCreate} />} placeholder={
<EmptyDataPlaceholder
buttonProps={{ title: 'organizations.create_organization', onClick: onCreate }}
/>
}
rowGroups={[{ key: 'data', data }]} rowGroups={[{ key: 'data', data }]}
rowClickHandler={({ id }) => { rowClickHandler={({ id }) => {
navigate(joinPath(pathname, id)); navigate(joinPath(pathname, id));

View file

@ -1,14 +1,18 @@
import { joinPath } from '@silverhand/essentials'; import { ReservedPlanId } from '@logto/schemas';
import { useCallback, useState } from 'react'; import { cond, conditional, joinPath } from '@silverhand/essentials';
import { useCallback, useContext, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import Plus from '@/assets/icons/plus.svg'; import Plus from '@/assets/icons/plus.svg';
import PageMeta from '@/components/PageMeta'; import PageMeta from '@/components/PageMeta';
import { subscriptionPage } from '@/consts/pages';
import { TenantsContext } from '@/contexts/TenantsProvider';
import Button from '@/ds-components/Button'; import Button from '@/ds-components/Button';
import Card from '@/ds-components/Card'; import Card from '@/ds-components/Card';
import CardTitle from '@/ds-components/CardTitle'; import CardTitle from '@/ds-components/CardTitle';
import TabNav, { TabNavItem } from '@/ds-components/TabNav'; import TabNav, { TabNavItem } from '@/ds-components/TabNav';
import useConfigs from '@/hooks/use-configs'; import useConfigs from '@/hooks/use-configs';
import useSubscriptionPlan from '@/hooks/use-subscription-plan';
import useTenantPathname from '@/hooks/use-tenant-pathname'; import useTenantPathname from '@/hooks/use-tenant-pathname';
import * as pageLayout from '@/scss/page-layout.module.scss'; import * as pageLayout from '@/scss/page-layout.module.scss';
@ -29,10 +33,18 @@ type Props = {
function Organizations({ tab }: Props) { function Organizations({ tab }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { currentTenantId, isDevTenant } = useContext(TenantsContext);
const { data: currentPlan } = useSubscriptionPlan(currentTenantId);
const { navigate } = useTenantPathname(); const { navigate } = useTenantPathname();
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const { configs, isLoading: isLoadingConfigs } = useConfigs(); const { configs, isLoading: isLoadingConfigs } = useConfigs();
const isInitialSetup = !isLoadingConfigs && !configs?.organizationCreated; const isInitialSetup = !isLoadingConfigs && !configs?.organizationCreated;
const isOrganizationsDisabled = currentPlan?.id === ReservedPlanId.Free;
const upgradePlan = useCallback(() => {
navigate(subscriptionPage);
}, [navigate]);
const handleCreate = useCallback(() => { const handleCreate = useCallback(() => {
if (isInitialSetup) { if (isInitialSetup) {
@ -56,7 +68,12 @@ function Organizations({ tab }: Props) {
/> />
<PageMeta titleKey="organizations.page_title" /> <PageMeta titleKey="organizations.page_title" />
<div className={pageLayout.headline}> <div className={pageLayout.headline}>
<CardTitle title="organizations.title" subtitle="organizations.subtitle" /> <CardTitle
isBeta
paywall={cond((isOrganizationsDisabled || isDevTenant) && ReservedPlanId.Hobby)}
title="organizations.title"
subtitle="organizations.subtitle"
/>
{!isInitialSetup && ( {!isInitialSetup && (
<Button <Button
icon={<Plus />} icon={<Plus />}
@ -69,7 +86,15 @@ function Organizations({ tab }: Props) {
</div> </div>
{isInitialSetup && ( {isInitialSetup && (
<Card className={styles.emptyCardContainer}> <Card className={styles.emptyCardContainer}>
<EmptyDataPlaceholder onCreate={handleCreate} /> <EmptyDataPlaceholder
buttonProps={{
title: isOrganizationsDisabled
? 'upsell.upgrade_plan'
: 'organizations.setup_organization',
onClick: isOrganizationsDisabled ? upgradePlan : handleCreate,
...conditional(isOrganizationsDisabled && { icon: undefined }),
}}
/>
</Card> </Card>
)} )}
{!isInitialSetup && ( {!isInitialSetup && (

View file

@ -16,7 +16,7 @@ const planQuotaGroupKeyPhraseMap: {
[SubscriptionPlanTableGroupKey.userAuthentication]: 'user_authn.title', [SubscriptionPlanTableGroupKey.userAuthentication]: 'user_authn.title',
[SubscriptionPlanTableGroupKey.roles]: 'user_management.title', [SubscriptionPlanTableGroupKey.roles]: 'user_management.title',
[SubscriptionPlanTableGroupKey.hooks]: 'hooks.title', [SubscriptionPlanTableGroupKey.hooks]: 'hooks.title',
[SubscriptionPlanTableGroupKey.organization]: 'organization.title', [SubscriptionPlanTableGroupKey.organizations]: 'organizations.title',
[SubscriptionPlanTableGroupKey.auditLogs]: 'audit_logs.title', [SubscriptionPlanTableGroupKey.auditLogs]: 'audit_logs.title',
[SubscriptionPlanTableGroupKey.support]: 'support.title', [SubscriptionPlanTableGroupKey.support]: 'support.title',
}; };

View file

@ -37,7 +37,7 @@ const planQuotaKeyPhraseMap: {
auditLogsRetentionDays: 'audit_logs.retention', auditLogsRetentionDays: 'audit_logs.retention',
communitySupportEnabled: 'support.community', communitySupportEnabled: 'support.community',
ticketSupportResponseTime: 'support.customer_ticket', ticketSupportResponseTime: 'support.customer_ticket',
organizationEnabled: 'organization.organization', organizationsEnabled: 'organizations.organizations',
}; };
type Props = { type Props = {

View file

@ -16,8 +16,6 @@ export type SubscriptionPlanQuota = Omit<
// Support // Support
communitySupportEnabled: boolean; communitySupportEnabled: boolean;
ticketSupportResponseTime: number; ticketSupportResponseTime: number;
// Organization
organizationEnabled: boolean;
// SSO // SSO
ssoEnabled: boolean; ssoEnabled: boolean;
}; };
@ -63,7 +61,7 @@ export enum SubscriptionPlanTableGroupKey {
roles = 'roles', roles = 'roles',
auditLogs = 'auditLogs', auditLogs = 'auditLogs',
hooks = 'hooks', hooks = 'hooks',
organization = 'organization', organizations = 'organizations',
support = 'support', support = 'support',
} }

View file

@ -6,7 +6,6 @@ import { tryReadResponseErrorBody } from '@/cloud/hooks/use-cloud-api';
import { type SubscriptionPlanResponse } from '@/cloud/types/router'; import { type SubscriptionPlanResponse } from '@/cloud/types/router';
import { import {
communitySupportEnabledMap, communitySupportEnabledMap,
organizationEnabledMap,
ssoEnabledMap, ssoEnabledMap,
ticketSupportResponseTimeMap, ticketSupportResponseTimeMap,
} from '@/consts/plan-quotas'; } from '@/consts/plan-quotas';
@ -22,7 +21,6 @@ export const addSupportQuotaToPlan = (subscriptionPlanResponse: SubscriptionPlan
...quota, ...quota,
communitySupportEnabled: communitySupportEnabledMap[id] ?? false, // Fallback to not supported communitySupportEnabled: communitySupportEnabledMap[id] ?? false, // Fallback to not supported
ticketSupportResponseTime: ticketSupportResponseTimeMap[id] ?? 0, // Fallback to not supported ticketSupportResponseTime: ticketSupportResponseTimeMap[id] ?? 0, // Fallback to not supported
organizationEnabled: organizationEnabledMap[id] ?? false, // Fallback to not supported
ssoEnabled: ssoEnabledMap[id] ?? false, // Fallback to not supported ssoEnabled: ssoEnabledMap[id] ?? false, // Fallback to not supported
}, },
}; };

View file

@ -91,7 +91,7 @@
"zod": "^3.22.4" "zod": "^3.22.4"
}, },
"devDependencies": { "devDependencies": {
"@logto/cloud": "0.2.5-d434baa", "@logto/cloud": "0.2.5-9257c0d",
"@silverhand/eslint-config": "4.0.1", "@silverhand/eslint-config": "4.0.1",
"@silverhand/ts-config": "4.0.0", "@silverhand/ts-config": "4.0.0",
"@types/debug": "^4.1.7", "@types/debug": "^4.1.7",

View file

@ -70,6 +70,7 @@ export const createQuotaLibrary = (
}, },
customDomainEnabled: notNumber, customDomainEnabled: notNumber,
mfaEnabled: notNumber, mfaEnabled: notNumber,
organizationsEnabled: notNumber,
omniSignInEnabled: notNumber, // No limit for now omniSignInEnabled: notNumber, // No limit for now
builtInEmailConnectorEnabled: notNumber, // No limit for now builtInEmailConnectorEnabled: notNumber, // No limit for now
}; };

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Unbegrenzte Webhooks', unlimited: 'Unbegrenzte Webhooks',
not_eligible: 'Entferne deine Webhooks', not_eligible: 'Entferne deine Webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Entferne deine Organisationen', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Audit-Log-Retention', name: 'Audit-Log-Retention',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: 'Organisation', title: 'Organisation',
organization: 'Organisation', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Support', title: 'Support',

View file

@ -1,5 +1,5 @@
const organization = { const organizations = {
require_membership: 'The user must be a member of the organization to proceed.', require_membership: 'The user must be a member of the organization to proceed.',
}; };
export default Object.freeze(organization); export default Object.freeze(organizations);

View file

@ -1,4 +1,4 @@
const organization = { const organizations = {
organization: 'Organization', organization: 'Organization',
page_title: 'Organizations', page_title: 'Organizations',
title: 'Organizations', title: 'Organizations',
@ -89,4 +89,4 @@ const organization = {
}, },
}; };
export default Object.freeze(organization); export default Object.freeze(organizations);

View file

@ -93,10 +93,10 @@ const quota_item = {
unlimited: 'Unlimited webhooks', unlimited: 'Unlimited webhooks',
not_eligible: 'Remove your webhooks', not_eligible: 'Remove your webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', name: 'Organizations',
limited: 'Organization', limited: 'Organizations',
unlimited: 'Organization', unlimited: 'Organizations',
not_eligible: 'Remove your organizations', not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {

View file

@ -51,9 +51,9 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: 'Organization', title: 'Organizations',
organization: 'Organization', organizations: 'Organizations',
}, },
support: { support: {
title: 'Support', title: 'Support',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Webhooks ilimitados', unlimited: 'Webhooks ilimitados',
not_eligible: 'Elimina tus webhooks', not_eligible: 'Elimina tus webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Elimine su organización', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Conservación de registros de auditoría', name: 'Conservación de registros de auditoría',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: 'Organización', title: 'Organización',
organization: 'Organización', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Soporte', title: 'Soporte',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Webhooks illimités', unlimited: 'Webhooks illimités',
not_eligible: 'Supprimez vos webhooks', not_eligible: 'Supprimez vos webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organisation', /** UNTRANSLATED */
limited: 'Organisation', name: 'Organizations',
unlimited: 'Organisation', /** UNTRANSLATED */
not_eligible: 'Supprimez vos organisations', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: "Conservation des journaux d'audit", name: "Conservation des journaux d'audit",

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: 'Organisation', title: 'Organisation',
organization: 'Organisation', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Support', title: 'Support',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Webhook illimitati', unlimited: 'Webhook illimitati',
not_eligible: 'Rimuovi i tuoi webhook', not_eligible: 'Rimuovi i tuoi webhook',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Rimuovi il tuo organisations', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Conservazione log di audit', name: 'Conservazione log di audit',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: 'Organizzazione', title: 'Organizzazione',
organization: 'Organizzazione', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Assistenza', title: 'Assistenza',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: '無制限のWebhooks', unlimited: '無制限のWebhooks',
not_eligible: 'Webhookを削除してください', not_eligible: 'Webhookを削除してください',
}, },
organization_enabled: { organizations_enabled: {
name: '組織', /** UNTRANSLATED */
limited: '組織', name: 'Organizations',
unlimited: '組織', /** UNTRANSLATED */
not_eligible: '組織を削除してください', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: '監査ログの保持期間', name: '監査ログの保持期間',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'ウェブフック', title: 'ウェブフック',
hooks: 'ウェブフック', hooks: 'ウェブフック',
}, },
organization: { organizations: {
title: '組織', title: '組織',
organization: '組織', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'サポート', title: 'サポート',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: '무제한 Webhooks', unlimited: '무제한 Webhooks',
not_eligible: '웹훅을 삭제하세요', not_eligible: '웹훅을 삭제하세요',
}, },
organization_enabled: { organizations_enabled: {
name: '조직', /** UNTRANSLATED */
limited: '조직', name: 'Organizations',
unlimited: '조직', /** UNTRANSLATED */
not_eligible: '조직을 제거하십시오', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: '감사 로그 보존 기간', name: '감사 로그 보존 기간',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: '조직', title: '조직',
organization: '조직', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: '지원', title: '지원',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Nieograniczona liczba webhooków', unlimited: 'Nieograniczona liczba webhooków',
not_eligible: 'Usuń swoje webhooki', not_eligible: 'Usuń swoje webhooki',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Usuń swoje organizacje', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Przechowywanie dzienników audytowych', name: 'Przechowywanie dzienników audytowych',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooki', title: 'Webhooki',
hooks: 'Webhooki', hooks: 'Webhooki',
}, },
organization: { organizations: {
title: 'Organizacja', title: 'Organizacja',
organization: 'Organizacja', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Wsparcie', title: 'Wsparcie',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Webhooks ilimitados', unlimited: 'Webhooks ilimitados',
not_eligible: 'Remova seus webhooks', not_eligible: 'Remova seus webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Remova suas organizações', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Permanência de registros de auditoria', name: 'Permanência de registros de auditoria',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: 'Organização', title: 'Organização',
organization: 'Organização', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Suporte', title: 'Suporte',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Webhooks ilimitados', unlimited: 'Webhooks ilimitados',
not_eligible: 'Remova os seus webhooks', not_eligible: 'Remova os seus webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organização', /** UNTRANSLATED */
limited: 'Organização', name: 'Organizations',
unlimited: 'Organização', /** UNTRANSLATED */
not_eligible: 'Remover as tuas organizações', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Conservação de registos de auditoria', name: 'Conservação de registos de auditoria',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Hooks', title: 'Hooks',
hooks: 'Hooks', hooks: 'Hooks',
}, },
organization: { organizations: {
title: 'Organização', title: 'Organização',
organization: 'Organização', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Suporte', title: 'Suporte',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Неограниченное количество вебхуков', unlimited: 'Неограниченное количество вебхуков',
not_eligible: 'Удалите ваши вебхуки', not_eligible: 'Удалите ваши вебхуки',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Удалите свои организации', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Время хранения аудит-логов', name: 'Время хранения аудит-логов',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Вебхуки', title: 'Вебхуки',
hooks: 'Вебхуки', hooks: 'Вебхуки',
}, },
organization: { organizations: {
title: 'Организация', title: 'Организация',
organization: 'Организация', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Поддержка', title: 'Поддержка',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: 'Sınırsız webhooklar', unlimited: 'Sınırsız webhooklar',
not_eligible: 'Webhooklarınızı kaldırın', not_eligible: 'Webhooklarınızı kaldırın',
}, },
organization_enabled: { organizations_enabled: {
name: 'Organization', /** UNTRANSLATED */
limited: 'Organization', name: 'Organizations',
unlimited: 'Organization', /** UNTRANSLATED */
not_eligible: 'Organizasyonlarınızı kaldırın', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: 'Denetim günlükleri saklama süresi', name: 'Denetim günlükleri saklama süresi',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Web Kancaları', title: 'Web Kancaları',
hooks: 'Web Kancaları', hooks: 'Web Kancaları',
}, },
organization: { organizations: {
title: 'Organizasyon', title: 'Organizasyon',
organization: 'Organization', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: 'Destek', title: 'Destek',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: '无限制的 Webhooks', unlimited: '无限制的 Webhooks',
not_eligible: '移除你的 Webhooks', not_eligible: '移除你的 Webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: '组织', /** UNTRANSLATED */
limited: '组织', name: 'Organizations',
unlimited: '组织', /** UNTRANSLATED */
not_eligible: '移除你的组织', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: '审计日志保留', name: '审计日志保留',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: '组织', title: '组织',
organization: '组织', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: '支持', title: '支持',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: '無限制的 Webhooks', unlimited: '無限制的 Webhooks',
not_eligible: '移除您的 Webhooks', not_eligible: '移除您的 Webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: '組織', /** UNTRANSLATED */
limited: '組織', name: 'Organizations',
unlimited: '組織', /** UNTRANSLATED */
not_eligible: '刪除您的組織', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: '審計日誌保留', name: '審計日誌保留',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: '組織', title: '組織',
organization: '組織', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: '支援', title: '支援',

View file

@ -93,11 +93,15 @@ const quota_item = {
unlimited: '無限制的 Webhooks', unlimited: '無限制的 Webhooks',
not_eligible: '移除您的 Webhooks', not_eligible: '移除您的 Webhooks',
}, },
organization_enabled: { organizations_enabled: {
name: '組織', /** UNTRANSLATED */
limited: '組織', name: 'Organizations',
unlimited: '組織', /** UNTRANSLATED */
not_eligible: '移除您的組織', limited: 'Organizations',
/** UNTRANSLATED */
unlimited: 'Organizations',
/** UNTRANSLATED */
not_eligible: 'Remove your organizations',
}, },
audit_logs_retention_days: { audit_logs_retention_days: {
name: '審計記錄保留期限', name: '審計記錄保留期限',

View file

@ -51,9 +51,10 @@ const quota_table = {
title: 'Webhooks', title: 'Webhooks',
hooks: 'Webhooks', hooks: 'Webhooks',
}, },
organization: { organizations: {
title: '組織', title: '組織',
organization: '組織', /** UNTRANSLATED */
organizations: 'Organizations',
}, },
support: { support: {
title: '支援', title: '支援',

View file

@ -1322,8 +1322,8 @@ importers:
specifier: ^29.5.0 specifier: ^29.5.0
version: 29.5.0 version: 29.5.0
'@logto/cloud': '@logto/cloud':
specifier: 0.2.5-d434baa specifier: 0.2.5-9257c0d
version: 0.2.5-d434baa(zod@3.22.4) version: 0.2.5-9257c0d(zod@3.22.4)
'@rollup/plugin-commonjs': '@rollup/plugin-commonjs':
specifier: ^25.0.0 specifier: ^25.0.0
version: 25.0.7(rollup@4.1.4) version: 25.0.7(rollup@4.1.4)
@ -2840,8 +2840,8 @@ importers:
specifier: workspace:^1.3.1 specifier: workspace:^1.3.1
version: link:../app-insights version: link:../app-insights
'@logto/cloud': '@logto/cloud':
specifier: 0.2.5-d434baa specifier: 0.2.5-9257c0d
version: 0.2.5-d434baa(zod@3.22.4) version: 0.2.5-9257c0d(zod@3.22.4)
'@logto/connector-kit': '@logto/connector-kit':
specifier: workspace:^2.0.0 specifier: workspace:^2.0.0
version: link:../toolkit/connector-kit version: link:../toolkit/connector-kit
@ -3318,8 +3318,8 @@ importers:
version: 3.22.4 version: 3.22.4
devDependencies: devDependencies:
'@logto/cloud': '@logto/cloud':
specifier: 0.2.5-d434baa specifier: 0.2.5-9257c0d
version: 0.2.5-d434baa(zod@3.22.4) version: 0.2.5-9257c0d(zod@3.22.4)
'@silverhand/eslint-config': '@silverhand/eslint-config':
specifier: 4.0.1 specifier: 4.0.1
version: 4.0.1(eslint@8.44.0)(prettier@3.0.0)(typescript@5.0.2) version: 4.0.1(eslint@8.44.0)(prettier@3.0.0)(typescript@5.0.2)
@ -7505,7 +7505,7 @@ packages:
resolution: {integrity: sha512-7I2ELo5UWIJsFCYK/gX465l0+QhXTdyYWkgb2CcdPu5KbaPBNpASedm+fEV2NREYe2svbNODFhog6UMA/xGQnQ==} resolution: {integrity: sha512-7I2ELo5UWIJsFCYK/gX465l0+QhXTdyYWkgb2CcdPu5KbaPBNpASedm+fEV2NREYe2svbNODFhog6UMA/xGQnQ==}
dependencies: dependencies:
'@logto/js': 2.1.1 '@logto/js': 2.1.1
'@silverhand/essentials': 2.8.4 '@silverhand/essentials': 2.8.5
camelcase-keys: 7.0.2 camelcase-keys: 7.0.2
jose: 4.14.4 jose: 4.14.4
dev: true dev: true
@ -7514,16 +7514,16 @@ packages:
resolution: {integrity: sha512-xq4LhQ6ItbukAHgsMDcgfspTpdpO5sSfSEugpOrGP/nLwzGTfBO78OSUfMdBQEDr5+3SRmONuSjUBBwssOLINA==} resolution: {integrity: sha512-xq4LhQ6ItbukAHgsMDcgfspTpdpO5sSfSEugpOrGP/nLwzGTfBO78OSUfMdBQEDr5+3SRmONuSjUBBwssOLINA==}
dependencies: dependencies:
'@logto/js': 2.1.3 '@logto/js': 2.1.3
'@silverhand/essentials': 2.8.4 '@silverhand/essentials': 2.8.5
camelcase-keys: 7.0.2 camelcase-keys: 7.0.2
jose: 4.14.4 jose: 4.14.4
dev: true dev: true
/@logto/cloud@0.2.5-d434baa(zod@3.22.4): /@logto/cloud@0.2.5-9257c0d(zod@3.22.4):
resolution: {integrity: sha512-VmWpqFzpWBrzJPQLvfe0bb7/XjF3lxs/rPbLt3zqBjGPtDXM9FAUn1m/gPbTwzXi5PxGOjlbD7jTl+3+1u4/NQ==} resolution: {integrity: sha512-tACUnAH8eRVS/InlIFIP50IcQzjlqCCPPa2Oqs4NNe1EkVMW7uplNZDPa1cTqT3fk223OvU9/9f0ydK9gwNZPQ==}
engines: {node: ^18.12.0} engines: {node: ^18.12.0}
dependencies: dependencies:
'@silverhand/essentials': 2.8.4 '@silverhand/essentials': 2.8.5
'@withtyped/server': 0.12.9(zod@3.22.4) '@withtyped/server': 0.12.9(zod@3.22.4)
transitivePeerDependencies: transitivePeerDependencies:
- zod - zod
@ -7540,7 +7540,7 @@ packages:
/@logto/js@2.1.3: /@logto/js@2.1.3:
resolution: {integrity: sha512-TOuoC5gHx/SfY5gcGSBfw63x5TpM6Lm/9J5y0Jy003Z1DZARUlpz0KbzyCVAIC/+6qIefkmNPHKl1rq9MB/hog==} resolution: {integrity: sha512-TOuoC5gHx/SfY5gcGSBfw63x5TpM6Lm/9J5y0Jy003Z1DZARUlpz0KbzyCVAIC/+6qIefkmNPHKl1rq9MB/hog==}
dependencies: dependencies:
'@silverhand/essentials': 2.8.4 '@silverhand/essentials': 2.8.5
camelcase-keys: 7.0.2 camelcase-keys: 7.0.2
jose: 4.14.4 jose: 4.14.4
dev: true dev: true
@ -9241,6 +9241,11 @@ packages:
resolution: {integrity: sha512-VaI00QyD2trA7n7/wHNcGNGRXoSr8dUGs/hQCu4Rju4Edl3vso7CeCXdfGU2aNDuT2uMs75of6Ph8gqVJhWlYQ==} resolution: {integrity: sha512-VaI00QyD2trA7n7/wHNcGNGRXoSr8dUGs/hQCu4Rju4Edl3vso7CeCXdfGU2aNDuT2uMs75of6Ph8gqVJhWlYQ==}
engines: {node: ^16.13.0 || ^18.12.0 || ^19.2.0, pnpm: ^8.0.0} engines: {node: ^16.13.0 || ^18.12.0 || ^19.2.0, pnpm: ^8.0.0}
/@silverhand/essentials@2.8.5:
resolution: {integrity: sha512-ChOGm1NeETWkEZXkIDD123cf8gxphqeSbtnvgsiqKMmWH2oSMvqORAyVFdngl0MuL9Ef5EdLFII+JrJkdytHrQ==}
engines: {node: ^16.13.0 || ^18.12.0 || ^19.2.0, pnpm: ^8.0.0}
dev: true
/@silverhand/ts-config-react@4.0.0(typescript@5.0.2): /@silverhand/ts-config-react@4.0.0(typescript@5.0.2):
resolution: {integrity: sha512-IkZka1iuIBgw0AUbsknghw1vOIs4zOgUxR8jL38Kuk63hmSj687N4BWBb8KhVMhqaG4U/DYkbSEZuwsyHBe68g==} resolution: {integrity: sha512-IkZka1iuIBgw0AUbsknghw1vOIs4zOgUxR8jL38Kuk63hmSj687N4BWBb8KhVMhqaG4U/DYkbSEZuwsyHBe68g==}
engines: {node: ^18.12.0} engines: {node: ^18.12.0}