Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: subscription page #30551

Merged
merged 94 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
f7ab26c
adding mock types and payloads
hugocostadev Sep 18, 2023
d0ad203
adding props to Card components
hugocostadev Sep 15, 2023
8f8cf24
add new admin page route
hugocostadev Sep 29, 2023
ad41c51
Manage subscription page
hugocostadev Sep 29, 2023
e59e531
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Sep 29, 2023
a51a7a1
plan name util
hugocostadev Oct 2, 2023
b46b39d
preventing UI error when using V2
hugocostadev Oct 2, 2023
4bf6b93
hasLicenseModule util
hugocostadev Oct 2, 2023
be9b575
useIsSelfHosted
hugocostadev Oct 2, 2023
b5c5416
Adding translations and UI improvements
hugocostadev Oct 2, 2023
f5b4bef
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 2, 2023
4a4179a
fix: Seats count card
hugocostadev Oct 4, 2023
de19a69
feat: Concurrent Users Card and changes
hugocostadev Oct 4, 2023
48abf03
feat: apps usage card
hugocostadev Oct 4, 2023
515ff18
fix: layout changes and fixes
hugocostadev Oct 4, 2023
75c21f2
feat: Checkout URL API and Integration with UpgradeButton
hugocostadev Oct 4, 2023
6375c8c
feat: adding new settings
hugocostadev Oct 4, 2023
2030211
new translations
hugocostadev Oct 4, 2023
8d28e28
fix: adding and updating external links
hugocostadev Oct 4, 2023
042037c
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 4, 2023
1a8381a
improving billing.checkoutUrl endpoint
hugocostadev Oct 4, 2023
7f97037
replacing license.get with license.info endpoint at useLicense hook
hugocostadev Oct 4, 2023
c1f57c4
fix: using new license.info endpoint
hugocostadev Oct 4, 2023
4037c2f
chore: improving getPlanName function
hugocostadev Oct 4, 2023
87960d2
fix: broken import
hugocostadev Oct 4, 2023
640597d
rebasing IServerInfo changes
hugocostadev Oct 4, 2023
c6f2926
Merge branch 'develop' into feat/manage-subscription
hugocostadev Oct 4, 2023
ef01d72
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 5, 2023
f460835
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 6, 2023
8f13569
improviments and implementations
hugocostadev Oct 6, 2023
efbc168
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 9, 2023
12dfa21
UX writer changes request
hugocostadev Oct 9, 2023
fbb8d0f
add: refetch license button
hugocostadev Oct 9, 2023
f458334
fix: testunit
hugocostadev Oct 9, 2023
19b20d8
Create brown-donuts-drive.md
hugocostadev Oct 9, 2023
6e2e1fc
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 9, 2023
c08d60f
fix: TS CI
hugocostadev Oct 9, 2023
bbee004
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 11, 2023
8be509b
remove unused i18n key
hugocostadev Oct 11, 2023
b454c9b
chore: improving useRegistrationStatus hook
hugocostadev Oct 13, 2023
393956a
fix url callbacks
hugocostadev Oct 13, 2023
7a4e55f
new cloud endpoint and sync implementation
hugocostadev Oct 13, 2023
eca3341
hook loading state and removing color token
hugocostadev Oct 13, 2023
c3bc1cb
changes requested
hugocostadev Oct 16, 2023
8da2029
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 16, 2023
84c53c7
fixing ts check
hugocostadev Oct 16, 2023
53d6ab0
fix ts check error
hugocostadev Oct 16, 2023
a86f99b
improving code
hugocostadev Oct 16, 2023
00d3c29
Merge branch 'develop' into feat/manage-subscription
hugocostadev Oct 17, 2023
7755f6f
fix: :bug: percentage error at AppsUsageCard
hugocostadev Oct 18, 2023
2f2bc89
fix: :bug: billing callbackUrl for development environments
hugocostadev Oct 18, 2023
d38c81d
fix: :wheelchair: aria-label to IconButton
hugocostadev Oct 18, 2023
546ba46
fix: :bug: MAC card infinite skeleton
hugocostadev Oct 18, 2023
f044dae
fix: :art: change Skeleton size
hugocostadev Oct 18, 2023
8c72c56
fix: :bug: plan selection scenarios
hugocostadev Oct 18, 2023
1e42f31
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 18, 2023
0dacfb2
fix: :bug: dev env callbackURL and improving error handling
hugocostadev Oct 18, 2023
2741b04
Merge branch 'develop' into feat/manage-subscription
hugocostadev Oct 18, 2023
ce71d47
fix: :bug: cloud.syncWorkspace permissionRequired
hugocostadev Oct 19, 2023
67610b1
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 19, 2023
f277fc8
fix: prevent plan name translation
hugocostadev Oct 20, 2023
c524e22
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 20, 2023
a09e99b
uselicensehook
hugocostadev Oct 20, 2023
7930f32
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 20, 2023
b67cf13
fix: :bug: updating useLicense usage to ManageSubscriptionPage
hugocostadev Oct 20, 2023
2ea999b
Merge branch 'develop' into feat/manage-subscription
hugocostadev Oct 23, 2023
b2d0701
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 23, 2023
918574f
fix: :bug: useLicense hook implementation
hugocostadev Oct 24, 2023
f5391f7
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 24, 2023
1ffc680
fix: :recycle: improving typo
hugocostadev Oct 24, 2023
1f95bab
refactor: :recycle: updating CE feature set
hugocostadev Oct 24, 2023
fae798e
refactor code to meet new requirements
hugocostadev Oct 25, 2023
8664b0a
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 25, 2023
e2ce48e
add new accessibility-certification module
hugocostadev Oct 25, 2023
d2c8a8c
adding limit validations
hugocostadev Oct 26, 2023
a3f61c0
Merge branch 'develop' into feat/manage-subscription
hugocostadev Oct 26, 2023
7241988
fix: :bug: replacing GET with POST method in cloud.syncWorkspace
hugocostadev Oct 27, 2023
ec3f387
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
hugocostadev Oct 27, 2023
067ba1e
Merge branch 'develop' into feat/manage-subscription
hugocostadev Oct 27, 2023
990c0b4
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
ggazzo Oct 27, 2023
1829c08
fix: rollback Card component changes
hugocostadev Oct 27, 2023
9f5d596
fix: reverting Card widget component changes
hugocostadev Oct 27, 2023
105fabd
fix: revert license token change
hugocostadev Oct 27, 2023
e0e2bb6
fix: :pencil2:
hugocostadev Oct 27, 2023
a0b6a3a
Merge remote-tracking branch 'origin/develop' into feat/manage-subscr…
ggazzo Oct 31, 2023
d870b4b
review
ggazzo Oct 31, 2023
0d25829
fix subscriptionSuccess
ggazzo Oct 31, 2023
590fc33
fix billing rest type location
ggazzo Oct 31, 2023
4477c68
rename endpoint billing to cloud
ggazzo Oct 31, 2023
2fbdaa8
comment related to endpoint
ggazzo Oct 31, 2023
7906d0b
remove unnecessary `space`
ggazzo Oct 31, 2023
18a8dd2
Allow Sync license if is registered or just subscribed
ggazzo Oct 31, 2023
6121c47
Update ee/packages/license/src/license.ts
ggazzo Oct 31, 2023
4652b7f
fix sync button
ggazzo Oct 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brown-donuts-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": minor
---

Added a new admin page called `Subscription`, this page is responsible of managing the current workspace subscription and it has a overview of the usage and limits of the plan
50 changes: 50 additions & 0 deletions apps/meteor/app/api/server/v1/cloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { check } from 'meteor/check';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { hasRoleAsync } from '../../../authorization/server/functions/hasRole';
import { getCheckoutUrl } from '../../../cloud/server/functions/getCheckoutUrl';
import { getConfirmationPoll } from '../../../cloud/server/functions/getConfirmationPoll';
import { registerPreIntentWorkspaceWizard } from '../../../cloud/server/functions/registerPreIntentWorkspaceWizard';
import { retrieveRegistrationStatus } from '../../../cloud/server/functions/retrieveRegistrationStatus';
import { saveRegistrationData } from '../../../cloud/server/functions/saveRegistrationData';
import { startRegisterWorkspaceSetupWizard } from '../../../cloud/server/functions/startRegisterWorkspaceSetupWizard';
import { syncWorkspace } from '../../../cloud/server/functions/syncWorkspace';
import { API } from '../api';

API.v1.addRoute(
Expand Down Expand Up @@ -126,3 +128,51 @@ API.v1.addRoute(
},
},
);

API.v1.addRoute(
'cloud.syncWorkspace',
{
authRequired: true,
permissionsRequired: ['manage-cloud'],
rateLimiterOptions: { numRequestsAllowed: 2, intervalTimeInMS: 60000 },
},
{
async post() {
try {
await syncWorkspace();

return API.v1.success({ success: true });
} catch (error) {
return API.v1.failure('Error during workspace sync');
}
},
},
);

/**
* Declaring endpoint here because we don't want this available to the sdk client
*/
declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Endpoints {
'/v1/cloud.checkoutUrl': {
GET: () => { url: string };
};
}
}

API.v1.addRoute(
'cloud.checkoutUrl',
{ authRequired: true, permissionsRequired: ['manage-cloud'] },
{
async get() {
const checkoutUrl = await getCheckoutUrl();

if (!checkoutUrl.url) {
return API.v1.failure();
}

return API.v1.success({ url: checkoutUrl.url });
},
},
);
45 changes: 45 additions & 0 deletions apps/meteor/app/cloud/server/functions/getCheckoutUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { serverFetch as fetch } from '@rocket.chat/server-fetch';

import { SystemLogger } from '../../../../server/lib/logger/system';
import { settings } from '../../../settings/server';
import { getURL } from '../../../utils/server/getURL';
import { getWorkspaceAccessTokenOrThrow } from './getWorkspaceAccessToken';

export const getCheckoutUrl = async () => {
try {
const token = await getWorkspaceAccessTokenOrThrow(false, 'workspace:billing');

const subscriptionURL = getURL('admin/subscription', {
full: true,
});

const body = {
okCallback: `${subscriptionURL}?subscriptionSuccess=true`,
cancelCallback: subscriptionURL,
};

const billingUrl = settings.get<string>('Cloud_Billing_Url');

const response = await fetch(`${billingUrl}/api/v2/checkout`, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
},
body,
});

if (!response.ok) {
throw new Error(await response.json());
Comment on lines +31 to +32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ggazzo I could not reproduce a scenario that got this error, because I didn't find a ok attribute in the payload ... Is this going to work? Maybe I've missed something

}

return response.json();
} catch (err: any) {
SystemLogger.error({
msg: 'Failed to get Checkout URL with Rocket.Chat Billing Service',
url: '/api/v2/checkout',
err,
});

throw err;
}
};
14 changes: 13 additions & 1 deletion apps/meteor/app/utils/lib/getURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,19 @@ export const _getURL = (
export const getURLWithoutSettings = (
path: string,
// eslint-disable-next-line @typescript-eslint/naming-convention
{ cdn = true, full = false, cloud = false, cloud_route = '', cloud_params = {} }: Record<string, any> = {},
{
cdn = true,
full = false,
cloud = false,
cloud_route = '',
cloud_params = {},
}: {
cdn?: boolean;
full?: boolean;
cloud?: boolean;
cloud_route?: string;
cloud_params?: Record<string, string>;
},
cdnPrefix: string,
siteUrl: string,
cloudDeepLinkUrl?: string,
Expand Down
8 changes: 7 additions & 1 deletion apps/meteor/app/utils/server/getURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { getURLWithoutSettings } from '../lib/getURL';

export const getURL = function (
path: string, // eslint-disable-next-line @typescript-eslint/naming-convention
params: Record<string, any> = {},
params: {
cdn?: boolean;
full?: boolean;
cloud?: boolean;
cloud_route?: string;
cloud_params?: Record<string, string>;
} = {},
cloudDeepLinkUrl?: string,
): string {
const cdnPrefix = settings.get<string>('CDN_PREFIX') || '';
Expand Down
16 changes: 3 additions & 13 deletions apps/meteor/client/hooks/useIsEnterprise.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import type { OperationResult } from '@rocket.chat/rest-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';

const queryKey = ['licenses', 'isEnterprise'] as const;

export const useIsEnterprise = (
options?: UseQueryOptions<
OperationResult<'GET', '/v1/licenses.isEnterprise'>,
unknown,
OperationResult<'GET', '/v1/licenses.isEnterprise'>,
typeof queryKey
>,
): UseQueryResult<OperationResult<'GET', '/v1/licenses.isEnterprise'>> => {
export const useIsEnterprise = (): UseQueryResult<OperationResult<'GET', '/v1/licenses.isEnterprise'>> => {
const isEnterpriseEdition = useEndpoint('GET', '/v1/licenses.isEnterprise');

return useQuery(queryKey, () => isEnterpriseEdition(), {
return useQuery(['licenses', 'isEnterprise'], () => isEnterpriseEdition(), {
keepPreviousData: true,
staleTime: Infinity,
...options,
});
};
9 changes: 9 additions & 0 deletions apps/meteor/client/hooks/useIsSelfHosted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useStatistics } from '../views/hooks/useStatistics';

export const useIsSelfHosted = (): { isSelfHosted: boolean; isLoading: boolean } => {
const { data, isLoading } = useStatistics();

const isSelfHosted = data?.deploy?.platform !== 'rocket-cloud';

return { isSelfHosted, isLoading };
};
8 changes: 6 additions & 2 deletions apps/meteor/client/hooks/useLicense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { useEffect } from 'react';

type LicenseDataType = Awaited<OperationResult<'GET', '/v1/licenses.info'>>['license'];

type LicenseParams = {
loadValues?: boolean;
};

const invalidateQueryClientLicenses = (() => {
let timeout: ReturnType<typeof setTimeout> | undefined;

Expand All @@ -19,7 +23,7 @@ const invalidateQueryClientLicenses = (() => {
};
})();

export const useLicense = (): UseQueryResult<Serialized<LicenseDataType>> => {
export const useLicense = (params?: LicenseParams): UseQueryResult<Serialized<LicenseDataType>> => {
const getLicenses = useEndpoint('GET', '/v1/licenses.info');

const queryClient = useQueryClient();
Expand All @@ -28,7 +32,7 @@ export const useLicense = (): UseQueryResult<Serialized<LicenseDataType>> => {

useEffect(() => notify('license', () => invalidateQueryClientLicenses(queryClient)), [notify, queryClient]);

return useQuery(['licenses', 'getLicenses'], () => getLicenses({}), {
return useQuery(['licenses', 'getLicenses', params?.loadValues], () => getLicenses({ ...params }), {
staleTime: Infinity,
keepPreviousData: true,
select: (data) => data.license,
Expand Down
10 changes: 8 additions & 2 deletions apps/meteor/client/hooks/useRegistrationStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { useEndpoint, usePermission } from '@rocket.chat/ui-contexts';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';

export const useRegistrationStatus = (): UseQueryResult<OperationResult<'GET', '/v1/cloud.registrationStatus'>> => {
type useRegistrationStatusReturnType = {
isRegistered?: boolean;
} & UseQueryResult<OperationResult<'GET', '/v1/cloud.registrationStatus'>>;

export const useRegistrationStatus = (): useRegistrationStatusReturnType => {
const getRegistrationStatus = useEndpoint('GET', '/v1/cloud.registrationStatus');
const canViewregistrationStatus = usePermission('manage-cloud');

return useQuery(
const queryResult = useQuery(
['getRegistrationStatus'],
() => {
if (!canViewregistrationStatus) {
Expand All @@ -20,4 +24,6 @@ export const useRegistrationStatus = (): UseQueryResult<OperationResult<'GET', '
staleTime: Infinity,
},
);

return { isRegistered: !queryResult.isLoading && queryResult.data?.registrationStatus?.workspaceRegistered, ...queryResult };
};
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => {
const isAdmin = useRole('admin');
const setModal = useSetModal();

const { data: registrationStatusData } = useRegistrationStatus();
const workspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false;
const { isRegistered } = useRegistrationStatus();

const handleRegisterWorkspaceClick = (): void => {
const handleModalClose = (): void => setModal(null);
Expand Down Expand Up @@ -102,10 +101,10 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => {
};
const adminItem: GenericMenuItemProps = {
id: 'registration',
content: workspaceRegistered ? t('Registration') : t('Register'),
content: isRegistered ? t('Registration') : t('Register'),
icon: 'cloud-plus',
onClick: () => {
if (workspaceRegistered) {
if (isRegistered) {
cloudRoute.push({ context: '/' });
return;
}
Expand Down
15 changes: 7 additions & 8 deletions apps/meteor/client/views/admin/cloud/RegisterWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ const RegisterWorkspace = () => {
const t = useTranslation();
const setModal = useSetModal();

const { data: registrationStatusData, isLoading, isError, refetch } = useRegistrationStatus();
const isWorkspaceRegistered = registrationStatusData?.registrationStatus?.workspaceRegistered ?? false;
const { isRegistered, isLoading, isError, refetch } = useRegistrationStatus();

if (isLoading || isError) {
return null;
Expand All @@ -26,7 +25,7 @@ const RegisterWorkspace = () => {
setModal(null);
refetch();
};
if (isWorkspaceRegistered) {
if (isRegistered) {
setModal(<ConnectWorkspaceModal onClose={handleModalClose} onStatusChange={refetch} />);
} else setModal(<RegisterWorkspaceModal onClose={handleModalClose} onStatusChange={refetch} />);
};
Expand All @@ -43,7 +42,7 @@ const RegisterWorkspace = () => {
<Page background='tint'>
<Page.Header title={t('Registration')}>
<RegisterWorkspaceMenu
isWorkspaceRegistered={isWorkspaceRegistered}
isWorkspaceRegistered={isRegistered || false}
onClick={handleRegisterWorkspaceClick}
onStatusChange={refetch}
onClickOfflineRegistration={handleManualWorkspaceRegistrationButton}
Expand All @@ -52,14 +51,14 @@ const RegisterWorkspace = () => {

<Page.ScrollableContentWithShadow>
<Box display='flex'>
{!isWorkspaceRegistered && <Tag variant='secondary-danger'>{t('RegisterWorkspace_NotRegistered_Title')}</Tag>}
{isWorkspaceRegistered && <Tag variant='primary'>{t('Workspace_registered')}</Tag>}
{!isRegistered && <Tag variant='secondary-danger'>{t('RegisterWorkspace_NotRegistered_Title')}</Tag>}
{isRegistered && <Tag variant='primary'>{t('Workspace_registered')}</Tag>}
</Box>

<Box pb={8}>
<Box fontScale='h3'>
{!isWorkspaceRegistered && t('RegisterWorkspace_NotRegistered_Subtitle')}
{isWorkspaceRegistered && t('RegisterWorkspace_Registered_Description')}
{!isRegistered && t('RegisterWorkspace_NotRegistered_Subtitle')}
{isRegistered && t('RegisterWorkspace_Registered_Description')}
</Box>
<RegisterWorkspaceCards />
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import { GenericResourceUsage, GenericResourceUsageSkeleton } from '../../../components/GenericResourceUsage';
import { useActiveConnections } from './hooks/useActiveConnections';
import { useActiveConnections } from '../../hooks/useActiveConnections';

const CustomUserActiveConnections = () => {
const t = useTranslation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import React from 'react';

import { ContextualbarContent, ContextualbarFooter } from '../../../components/Contextualbar';
import { useIsEnterprise } from '../../../hooks/useIsEnterprise';
import { useActiveConnections } from './hooks/useActiveConnections';
import { useActiveConnections } from '../../hooks/useActiveConnections';

const CustomUserStatusService = () => {
const t = useTranslation();
Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/client/views/admin/info/InformationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import type { IInstance } from '@rocket.chat/rest-typings';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React, { memo } from 'react';

import SeatsCard from '../../../../ee/client/views/admin/info/SeatsCard';
import { useSeatsCap } from '../../../../ee/client/views/admin/users/useSeatsCap';
import Page from '../../../components/Page';
import { useIsEnterprise } from '../../../hooks/useIsEnterprise';
import SeatsCard from '../subscription/components/cards/SeatsCard';
import DeploymentCard from './DeploymentCard';
import LicenseCard from './LicenseCard';
import UsageCard from './UsageCard';
Expand Down Expand Up @@ -95,9 +95,9 @@ const InformationPage = memo(function InformationPage({
<Grid.Item xl={12} height={!showSeatCap ? '50%' : 'full'}>
<LicenseCard />
</Grid.Item>
{!showSeatCap && (
{seatsCap && seatsCap.maxActiveUsers !== Infinity && (
<Grid.Item xl={12} height='50%'>
<SeatsCard seatsCap={seatsCap} />
<SeatsCard value={seatsCap.activeUsers} max={seatsCap.maxActiveUsers} />
</Grid.Item>
)}
</Grid.Item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { ComponentProps } from 'react';
import React from 'react';

import { useAutoSequence } from '../../../stories/hooks/useAutoSequence';
import UsagePieGraph from './UsagePieGraph';
import UsagePieGraph from '../subscription/components/UsagePieGraph';

export default {
title: 'Admin/Info/UsagePieGraph',
Expand Down
9 changes: 9 additions & 0 deletions apps/meteor/client/views/admin/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ declare module '@rocket.chat/ui-contexts' {
pathname: `/admin/moderation${`/${string}` | ''}${`/${string}` | ''}`;
pattern: '/admin/moderation/:context?/:id?';
};
'subscription': {
pathname: `/admin/subscription`;
pattern: '/admin/subscription';
};
}
}

Expand Down Expand Up @@ -237,3 +241,8 @@ registerAdminRoute('/device-management/:context?/:id?', {
name: 'device-management',
component: lazy(() => import('../../../ee/client/views/admin/deviceManagement/DeviceManagementAdminRoute')),
});

registerAdminRoute('/subscription', {
name: 'subscription',
component: lazy(() => import('./subscription/SubscriptionRoute')),
});
6 changes: 6 additions & 0 deletions apps/meteor/client/views/admin/sidebarItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export const {
icon: 'info-circled',
permissionGranted: (): boolean => hasPermission('view-statistics'),
},
{
href: '/admin/subscription',
i18nLabel: 'Subscription',
icon: 'card',
permissionGranted: (): boolean => hasPermission('manage-cloud'),
},
{
href: '/admin/registration',
i18nLabel: 'Registration',
Expand Down
Loading