Skip to content

Commit 9ec3f7b

Browse files
authored
feat(console): init enterprise subscription page (#8127)
* feat(console): add enterprise subscription management page add enterprise subscritpion management page # Conflicts: # pnpm-lock.yaml * chore(console): bump cloud version bump cloud version * fix(console): add i18n phrase add i18n phrase
1 parent 9eac78e commit 9ec3f7b

File tree

109 files changed

+2485
-47
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+2485
-47
lines changed

packages/connectors/connector-logto-email/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"access": "public"
5353
},
5454
"devDependencies": {
55-
"@logto/cloud": "0.2.5-9c40a76",
55+
"@logto/cloud": "0.2.5-2553f41",
5656
"@silverhand/eslint-config": "6.0.1",
5757
"@silverhand/ts-config": "6.0.0",
5858
"@types/node": "^22.14.0",

packages/console/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@fontsource/roboto-mono": "^5.0.0",
2929
"@inkeep/cxkit-react": "^0.5.66",
3030
"@jest/types": "^29.5.0",
31-
"@logto/cloud": "0.2.5-9c40a76",
31+
"@logto/cloud": "0.2.5-2553f41",
3232
"@logto/connector-kit": "workspace:^",
3333
"@logto/core-kit": "workspace:^",
3434
"@logto/language-kit": "workspace:^",
Lines changed: 4 additions & 0 deletions
Loading

packages/console/src/cloud/types/router.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,16 @@ export type TenantMemberResponse = GetArrayElementType<
6060
export type TenantInvitationResponse = GetArrayElementType<
6161
GuardedResponse<GetTenantAuthRoutes['/api/tenants/:tenantId/invitations']>
6262
>;
63+
// End of the auth routes types
6364

6465
export type RegionResponse = GetArrayElementType<
6566
GuardedResponse<GetRoutes['/api/me/regions']>['regions']
6667
>;
67-
// End of the auth routes types
68+
69+
export type LogtoEnterpriseResponse = GetArrayElementType<
70+
GuardedResponse<GetRoutes['/api/me/logto-enterprises']>['logtoEnterprises']
71+
>;
72+
73+
export type LogtoEnterpriseSubscriptionResponse = GuardedResponse<
74+
GetRoutes['/api/me/logto-enterprises/:id']
75+
>;

packages/console/src/components/MauTokenExceededModal/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ function MauTokenExceededModal() {
4545
currentTenant && {
4646
mauLimit: currentTenant.usage.activeUsers,
4747
tokenLimit: currentTenant.usage.tokenUsage,
48+
userTokenLimit: currentTenant.usage.userTokenUsage,
49+
m2mTokenLimit: currentTenant.usage.m2mTokenUsage,
4850
}
4951
),
5052
[currentTenant]

packages/console/src/components/PlanUsage/PlanUsageCard/index.tsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,28 +13,13 @@ import IconButton from '@/ds-components/IconButton';
1313
import Tag from '@/ds-components/Tag';
1414
import TextLink from '@/ds-components/TextLink';
1515
import { ToggleTip } from '@/ds-components/Tip';
16+
import { formatQuotaNumber } from '@/utils/number';
1617
import { isPaidPlan } from '@/utils/subscription';
1718

1819
import { formatNumber } from '../utils';
1920

2021
import styles from './index.module.scss';
2122

22-
const formatQuotaNumber = (number: number): string => {
23-
if (number >= 1e6) {
24-
return (number / 1e6).toFixed(1) + 'M';
25-
}
26-
27-
if (number >= 1e3) {
28-
return (number / 1e3).toFixed(1) + 'K';
29-
}
30-
31-
if (Number.isInteger(number)) {
32-
return number.toString();
33-
}
34-
35-
return number.toFixed(2);
36-
};
37-
3823
const formatNumberTypedUsageDescription = ({
3924
usage,
4025
quota,

packages/console/src/components/PlanUsage/utils.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ import { isProPlan } from '@/utils/subscription';
3030
* two quotas.
3131
* So we need to manually define it here, and calculate the status based on the two quotas.
3232
*/
33-
enum CustomUsageKey {
33+
export enum CustomUsageKey {
3434
RbacEnabled = 'rbacEnabled',
3535
}
3636

37-
type UsageKey =
37+
export type UsageKey =
3838
| keyof Pick<
3939
SubscriptionQuota,
4040
| 'mauLimit'
@@ -70,6 +70,13 @@ export const usageKeys: UsageKey[] = [
7070
'securityFeaturesEnabled',
7171
];
7272

73+
export const featureEnablementUsageKeys: UsageKey[] = [
74+
'mfaEnabled',
75+
CustomUsageKey.RbacEnabled,
76+
'securityFeaturesEnabled',
77+
'organizationsLimit',
78+
];
79+
7380
export const usageKeyPriceMap: Record<UsageKey, number> = {
7481
mauLimit: 0,
7582
organizationsLimit: organizationAddOnUnitPrice,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import useSWR from 'swr';
2+
3+
import CardIcon from '@/assets/icons/card.svg?react';
4+
import { useCloudApi } from '@/cloud/hooks/use-cloud-api';
5+
import { type LogtoEnterpriseResponse } from '@/cloud/types/router';
6+
import DynamicT from '@/ds-components/DynamicT';
7+
import TextLink from '@/ds-components/TextLink';
8+
9+
import styles from '../index.module.scss';
10+
11+
type Props = {
12+
readonly className?: string;
13+
};
14+
15+
function EnterpriseSubscriptions({ className }: Props) {
16+
const cloudApi = useCloudApi();
17+
18+
const { data } = useSWR<{ logtoEnterprises: LogtoEnterpriseResponse[] }, Error>(
19+
'/api/me/logto-enterprises',
20+
async () => cloudApi.get('/api/me/logto-enterprises')
21+
);
22+
23+
if (!data || data.logtoEnterprises.length === 0) {
24+
return null;
25+
}
26+
27+
// Currently only support one enterprise subscription per user
28+
// If there are multiple, consider adding a dropdown selector in the future
29+
const defaultEnterpriseSubscription = data.logtoEnterprises[0];
30+
31+
if (!defaultEnterpriseSubscription) {
32+
return null;
33+
}
34+
35+
return (
36+
<TextLink
37+
to={`enterprise-subscriptions/${defaultEnterpriseSubscription.id}`}
38+
className={styles.button}
39+
icon={<CardIcon className={styles.icon} />}
40+
>
41+
<DynamicT forKey="topbar.subscription" />
42+
</TextLink>
43+
);
44+
}
45+
46+
export default EnterpriseSubscriptions;

packages/console/src/components/Topbar/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import DocumentIcon from '@/assets/icons/document-nav-button.svg?react';
99
import CloudLogo from '@/assets/images/cloud-logo.svg?react';
1010
import Logo from '@/assets/images/logo.svg?react';
1111
import { githubReleasesLink } from '@/consts';
12-
import { isCloud } from '@/consts/env';
12+
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
1313
import DynamicT from '@/ds-components/DynamicT';
1414
import Spacer from '@/ds-components/Spacer';
1515
import TextLink from '@/ds-components/TextLink';
@@ -18,6 +18,7 @@ import useTenantPathname from '@/hooks/use-tenant-pathname';
1818
import { onKeyDownHandler } from '@/utils/a11y';
1919

2020
import ContactModal from './ContactModal';
21+
import EnterpriseSubscriptions from './EnterpriseSubscriptions';
2122
import InkeepAskAi from './InkeepAskAi';
2223
import TenantSelector from './TenantSelector';
2324
import UserInfo from './UserInfo';
@@ -54,6 +55,8 @@ function Topbar({ className, hideTenantSelector, hideTitle }: Props) {
5455
)}
5556
<Spacer />
5657
{isCloud && <InkeepAskAi className={styles.button} />}
58+
{/* TODO: Remove the dev features flag check when enterprise subscription is generally available */}
59+
{isCloud && isDevFeaturesEnabled && <EnterpriseSubscriptions className={styles.button} />}
5760
<DocumentButton />
5861
<HelpButton />
5962
{!isCloud && <VersionButton />}

packages/console/src/consts/page-tabs.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,8 @@ export enum OrganizationRoleDetailsTabs {
6060
Permissions = 'permissions',
6161
General = 'general',
6262
}
63+
64+
export enum EnterpriseSubscriptionTabs {
65+
Subscription = 'subscription',
66+
BillingHistory = 'billing-history',
67+
}

0 commit comments

Comments
 (0)