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: workspace info admin page #30434

Merged
merged 64 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
98438fe
chore: improve deprecation warning
dougfabris Sep 8, 2023
f6277d6
replacing Card component to naming imports
hugocostadev Sep 8, 2023
91f10e3
fix typo
hugocostadev Sep 8, 2023
2bea3c3
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Sep 11, 2023
965018a
adding props to Card components
hugocostadev Sep 15, 2023
d3424a0
updating deps
hugocostadev Sep 18, 2023
b3d1798
adding mock types and payloads
hugocostadev Sep 18, 2023
6991c32
new workspace status page
hugocostadev Sep 18, 2023
f8ece5c
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Sep 18, 2023
914638b
cleaning unused components
hugocostadev Sep 18, 2023
e184f5c
change request
hugocostadev Sep 19, 2023
738b28d
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Sep 19, 2023
de0c449
feat: adding discussion statistics
hugocostadev Sep 20, 2023
88494cb
refactoring VersionCard and spliting into separated components
hugocostadev Sep 20, 2023
e7ff4c2
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Sep 20, 2023
f9f0f07
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 4, 2023
05b117f
replacing license.get with license.info endpoint at useLicense hook
hugocostadev Oct 4, 2023
bf3cb21
feat: FramedIcon component
hugocostadev Oct 5, 2023
6ca4a8a
chore: IWorkspaceInfo type def
hugocostadev Oct 5, 2023
d496a83
integrating backend with frontend and renaming folder/files
hugocostadev Oct 5, 2023
4409dec
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 5, 2023
d8fb6e8
updating go links
hugocostadev Oct 6, 2023
5b0460d
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 9, 2023
7482034
Implementing license limits check FE
hugocostadev Oct 9, 2023
dc78308
fix: ts errors
hugocostadev Oct 9, 2023
4529843
fix: unit test
hugocostadev Oct 9, 2023
e3517b9
fix: unit test
hugocostadev Oct 9, 2023
d33eef5
Create workspace-status-admin-page.md
hugocostadev Oct 9, 2023
d72b6a1
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 9, 2023
7053533
fix: e2e test at workspace-status
hugocostadev Oct 10, 2023
1987da7
fix: route fallback for mobile apps
hugocostadev Oct 10, 2023
9643672
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 10, 2023
6b7dfef
fix: e2e tests
hugocostadev Oct 10, 2023
a755612
fix: e2e test
hugocostadev Oct 10, 2023
f947a88
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 10, 2023
78599bd
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 10, 2023
a6bbb48
Update .changeset/workspace-status-admin-page.md
hugocostadev Oct 11, 2023
45a1429
fixing tab size
hugocostadev Oct 11, 2023
7a42f1a
fix tab size
hugocostadev Oct 11, 2023
5f34548
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 11, 2023
2c2f1cb
fixing invalid date error
hugocostadev Oct 11, 2023
4814a38
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 16, 2023
6debd1e
changes request
hugocostadev Oct 16, 2023
98fd4fa
fix: no license scenario
hugocostadev Oct 16, 2023
a7b8ba1
fix version check
hugocostadev Oct 16, 2023
8a5e9dd
fix: rename Workspace status to Workspace
hugocostadev Oct 17, 2023
59b63bf
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 17, 2023
769e341
renaming folder and components to Workspace
hugocostadev Oct 17, 2023
32844c5
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 20, 2023
051e887
Implementing useLicense chages
hugocostadev Oct 23, 2023
6ca2f66
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 23, 2023
2ca720c
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 27, 2023
ed5ac73
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 27, 2023
4df19b1
fix: typecheck error
hugocostadev Oct 27, 2023
05a0605
revert: :art: card component changes
hugocostadev Oct 27, 2023
c5d6513
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 27, 2023
279726c
fix: :pencil2: implementing background to Card component and fixing t…
hugocostadev Oct 27, 2023
fa9d9e5
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 30, 2023
f0a4307
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 30, 2023
0f1e49d
fix: homepage > cards e2e test
hugocostadev Oct 30, 2023
cf47b6f
Merge remote-tracking branch 'origin/develop' into feature/info-page-…
hugocostadev Oct 31, 2023
39d1865
fix:
hugocostadev Oct 31, 2023
9fc2ae7
Merge branch 'develop' into feature/info-page-overview-cards
hugocostadev Oct 31, 2023
1a630f2
review
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/workspace-status-admin-page.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 `Workspace info` in place of Information page, to make it easier to check the license
11 changes: 3 additions & 8 deletions apps/meteor/app/api/server/lib/getServerInfo.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import type { IWorkspaceInfo } from '@rocket.chat/core-typings';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import {
getCachedSupportedVersionsToken,
wrapPromise,
} from '../../../cloud/server/functions/supportedVersionsToken/supportedVersionsToken';
import { Info, minimumClientVersions } from '../../../utils/rocketchat.info';

type ServerInfo = {
info?: typeof Info;
supportedVersions?: { signed: string };
minimumClientVersions: typeof minimumClientVersions;
version: string;
};

const removePatchInfo = (version: string): string => version.replace(/(\d+\.\d+).*/, '$1');

export async function getServerInfo(userId?: string): Promise<ServerInfo> {
export async function getServerInfo(userId?: string): Promise<IWorkspaceInfo> {
const hasPermissionToViewStatistics = userId && (await hasPermissionAsync(userId, 'view-statistics'));
const supportedVersionsToken = await wrapPromise(getCachedSupportedVersionsToken());

Expand Down
33 changes: 23 additions & 10 deletions apps/meteor/app/statistics/server/lib/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,18 +305,30 @@ export const statistics = {
);

// Message statistics
statistics.totalChannelMessages = (await Rooms.findByType('c', { projection: { msgs: 1 } }).toArray()).reduce(
function _countChannelMessages(num: number, room: IRoom) {
const channels = await Rooms.findByType('c', { projection: { msgs: 1, prid: 1 } }).toArray();
const totalChannelDiscussionsMessages = await channels.reduce(function _countChannelDiscussionsMessages(num: number, room: IRoom) {
return num + (room.prid ? room.msgs : 0);
}, 0);
statistics.totalChannelMessages =
(await channels.reduce(function _countChannelMessages(num: number, room: IRoom) {
return num + room.msgs;
},
0,
);
statistics.totalPrivateGroupMessages = (await Rooms.findByType('p', { projection: { msgs: 1 } }).toArray()).reduce(
function _countPrivateGroupMessages(num: number, room: IRoom) {
}, 0)) - totalChannelDiscussionsMessages;

const privateGroups = await Rooms.findByType('p', { projection: { msgs: 1, prid: 1 } }).toArray();
const totalPrivateGroupsDiscussionsMessages = await privateGroups.reduce(function _countPrivateGroupsDiscussionsMessages(
num: number,
room: IRoom,
) {
return num + (room.prid ? room.msgs : 0);
},
0);
statistics.totalPrivateGroupMessages =
(await privateGroups.reduce(function _countPrivateGroupMessages(num: number, room: IRoom) {
return num + room.msgs;
},
0,
);
}, 0)) - totalPrivateGroupsDiscussionsMessages;

statistics.totalDiscussionsMessages = totalPrivateGroupsDiscussionsMessages + totalChannelDiscussionsMessages;

statistics.totalDirectMessages = (await Rooms.findByType('d', { projection: { msgs: 1 } }).toArray()).reduce(
function _countDirectMessages(num: number, room: IRoom) {
return num + room.msgs;
Expand All @@ -332,6 +344,7 @@ export const statistics = {
statistics.totalMessages =
statistics.totalChannelMessages +
statistics.totalPrivateGroupMessages +
statistics.totalDiscussionsMessages +
statistics.totalDirectMessages +
statistics.totalLivechatMessages;

Expand Down
6 changes: 3 additions & 3 deletions apps/meteor/client/hooks/useLicense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { useEndpoint, usePermission } from '@rocket.chat/ui-contexts';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';

export const useLicense = (): UseQueryResult<OperationResult<'GET', '/v1/licenses.get'>> => {
const getLicenses = useEndpoint('GET', '/v1/licenses.get');
export const useLicense = (): UseQueryResult<OperationResult<'GET', '/v1/licenses.info'>> => {
const getLicenses = useEndpoint('GET', '/v1/licenses.info');
const canViewLicense = usePermission('view-privileged-setting');

return useQuery(
Expand All @@ -13,7 +13,7 @@ export const useLicense = (): UseQueryResult<OperationResult<'GET', '/v1/license
if (!canViewLicense) {
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('unauthorized api call');
}
return getLicenses();
return getLicenses({ loadValues: true });
},
{
staleTime: Infinity,
Expand Down
29 changes: 29 additions & 0 deletions apps/meteor/client/hooks/useWorkspaceInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { IWorkspaceInfo, IStats } from '@rocket.chat/core-typings';
import type { IInstance } from '@rocket.chat/rest-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useQueries } from '@tanstack/react-query';

export const useWorkspaceInfo = () => {
const getStatistics = useEndpoint('GET', '/v1/statistics');
const getInstances = useEndpoint('GET', '/v1/instances.get');
const getServerInfo = useEndpoint('GET', '/info');

const results = useQueries({
queries: [
{ queryKey: ['info', 'serverInfo'], queryFn: () => getServerInfo(), staleTime: Infinity, keepPreviousData: true },
{ queryKey: ['info', 'instances'], queryFn: () => getInstances(), staleTime: Infinity, keepPreviousData: true },
{ queryKey: ['info', 'statistics'], queryFn: () => getStatistics({ refresh: 'true' }), staleTime: Infinity, keepPreviousData: true },
],
});

const [serverInfoQuery, instancesQuery, statisticsQuery] = results;

const instances: IInstance[] | undefined = instancesQuery?.data?.instances as IInstance[];
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
const statistics: IStats | undefined = statisticsQuery?.data as IStats;
const serverInfo: IWorkspaceInfo | undefined = serverInfoQuery?.data as IWorkspaceInfo;

const isLoading = results.some((query) => query.isLoading);
const isError = results.some((query) => query.isError);

return { instances, statistics, serverInfo, isLoading, isError, refetchStatistics: statisticsQuery.refetch };
};
22 changes: 22 additions & 0 deletions apps/meteor/client/lib/utils/isOverLicenseLimits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { LicenseLimitKind } from '@rocket.chat/license';

type Limits = Record<
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
LicenseLimitKind,
{
max: number;
value?: number;
}
>;

export const isOverLicenseLimits = (limits: Limits): boolean => {
for (const key in limits) {
if (Object.hasOwn(limits, key)) {
const limit = limits[key as keyof Limits];
if (limit.value !== undefined && limit.value > limit.max) {
return true;
}
}
}

return false;
};
hugocostadev marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
export const isOverLicenseLimits = (limits: Limits): boolean => {
for (const key in limits) {
if (Object.hasOwn(limits, key)) {
const limit = limits[key as keyof Limits];
if (limit.value !== undefined && limit.value > limit.max) {
return true;
}
}
}
return false;
};
export const isOverLicenseLimits = (limits: Limits): boolean =>
Object.values(limits).some((limit) => limit.value !== undefined && limit.value > limit.max);

Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,75 @@ import { renderHook } from '@testing-library/react-hooks';

import { useAdministrationItems } from './useAdministrationItems';

const LICENSE_INFO_PAYLOAD = {
data: {
license: {
version: '3.0',
information: {
autoRenew: false,
visualExpiration: '0001-01-01T00:00:00.000Z',
trial: false,
offline: false,
createdAt: '2023-10-09T17:25:54.828Z',
grantedBy: {
method: 'manual',
seller: 'V2',
},
tags: [
{
name: 'Pro',
color: '#F3BE08',
},
],
},
validation: {
serverUrls: [
{
value: 'localhost:3000',
type: 'regex',
},
],
validPeriods: [
{
validUntil: '2023-11-17T00:00:00.000Z',
invalidBehavior: 'invalidate_license',
},
],
statisticsReport: {
required: true,
},
},
grantedModules: [],
limits: {
activeUsers: [
{
max: 25,
behavior: 'prevent_action',
},
],
},
cloudMeta: {
trial: false,
trialEnd: '0001-01-01T00:00:00Z',
workspaceId: '651c4519f712c20001151d0a',
},
},
activeModules: [],
limits: {
activeUsers: {
value: 10,
max: 25,
},
},
},
success: true,
};

it('should not show upgrade item if has license and not have trial', async () => {
const license = { ...LICENSE_INFO_PAYLOAD, data: { ...LICENSE_INFO_PAYLOAD.data, activeModules: ['testModules'] } };
const { result, waitFor } = renderHook(() => useAdministrationItems(), {
wrapper: mockAppRoot()
.withEndpoint('GET', '/v1/licenses.get', () => ({
licenses: [
{
modules: ['testModule'],
meta: { trial: false },
} as any,
],
}))
.withEndpoint('GET', '/v1/licenses.info', () => license as any)
.withEndpoint('GET', '/v1/cloud.registrationStatus', () => ({
registrationStatus: {
workspaceRegistered: false,
Expand All @@ -32,13 +90,7 @@ it('should not show upgrade item if has license and not have trial', async () =>
it('should return an upgrade item if not have license or if have a trial', async () => {
const { result, waitFor } = renderHook(() => useAdministrationItems(), {
wrapper: mockAppRoot()
.withEndpoint('GET', '/v1/licenses.get', () => ({
licenses: [
{
modules: [],
} as any,
],
}))
.withEndpoint('GET', '/v1/licenses.info', () => LICENSE_INFO_PAYLOAD as any)
.withEndpoint('GET', '/v1/cloud.registrationStatus', () => ({
registrationStatus: {
workspaceRegistered: false,
Expand All @@ -62,13 +114,7 @@ it('should return an upgrade item if not have license or if have a trial', async
it('should return omnichannel item if has `view-livechat-manager` permission ', async () => {
const { result, waitFor } = renderHook(() => useAdministrationItems(), {
wrapper: mockAppRoot()
.withEndpoint('GET', '/v1/licenses.get', () => ({
licenses: [
{
modules: [],
} as any,
],
}))
.withEndpoint('GET', '/v1/licenses.info', () => LICENSE_INFO_PAYLOAD as any)
.withEndpoint('GET', '/v1/cloud.registrationStatus', () => ({
registrationStatus: {
workspaceRegistered: false,
Expand All @@ -90,13 +136,7 @@ it('should return omnichannel item if has `view-livechat-manager` permission ',
it('should show administration item if has at least one admin permission', async () => {
const { result, waitFor } = renderHook(() => useAdministrationItems(), {
wrapper: mockAppRoot()
.withEndpoint('GET', '/v1/licenses.get', () => ({
licenses: [
{
modules: [],
} as any,
],
}))
.withEndpoint('GET', '/v1/licenses.info', () => LICENSE_INFO_PAYLOAD as any)
.withEndpoint('GET', '/v1/cloud.registrationStatus', () => ({
registrationStatus: {
workspaceRegistered: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ export const useAdministrationItems = (): GenericMenuItemProps[] => {
const isAdmin = useRole('admin');
const setModal = useSetModal();

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

const handleRegisterWorkspaceClick = (): void => {
const handleModalClose = (): void => setModal(null);
Expand Down
13 changes: 0 additions & 13 deletions apps/meteor/client/views/admin/info/Feature.js

This file was deleted.

26 changes: 0 additions & 26 deletions apps/meteor/client/views/admin/info/Feature.stories.tsx

This file was deleted.

Loading
Loading