Skip to content

Commit

Permalink
refactor: unified users page header content into a single component (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksandernsilva authored Oct 15, 2024
1 parent 956bf61 commit 72c6101
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 69 deletions.
26 changes: 4 additions & 22 deletions apps/meteor/client/views/admin/users/AdminUsersPage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { LicenseInfo } from '@rocket.chat/core-typings';
import { Button, ButtonGroup, Callout, ContextualbarIcon, Icon, Skeleton, Tabs, TabsItem } from '@rocket.chat/fuselage';
import { Callout, ContextualbarIcon, Icon, Skeleton, Tabs, TabsItem } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import type { OptionProp } from '@rocket.chat/ui-client';
import { ExternalLink } from '@rocket.chat/ui-client';
import { usePermission, useRouteParameter, useTranslation, useRouter, useEndpoint } from '@rocket.chat/ui-contexts';
import { useRouteParameter, useTranslation, useRouter, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import React, { useEffect, useMemo, useRef, useState } from 'react';
Expand All @@ -28,7 +28,7 @@ import AdminUserForm from './AdminUserForm';
import AdminUserFormWithData from './AdminUserFormWithData';
import AdminUserInfoWithData from './AdminUserInfoWithData';
import AdminUserUpgrade from './AdminUserUpgrade';
import UserPageHeaderContentWithSeatsCap from './UserPageHeaderContentWithSeatsCap';
import UsersPageHeaderContent from './UsersPageHeaderContent';
import UsersTable from './UsersTable';
import useFilteredUsers from './hooks/useFilteredUsers';
import usePendingUsersCount from './hooks/usePendingUsersCount';
Expand Down Expand Up @@ -56,9 +56,6 @@ const AdminUsersPage = (): ReactElement => {
const context = useRouteParameter('context');
const id = useRouteParameter('id');

const canCreateUser = usePermission('create-user');
const canBulkCreateUser = usePermission('bulk-register-user');

const isCreateUserDisabled = useShouldPreventAction('activeUsers');

const getRoles = useEndpoint('GET', '/v1/roles.list');
Expand Down Expand Up @@ -111,22 +108,7 @@ const AdminUsersPage = (): ReactElement => {
<Page flexDirection='row'>
<Page>
<PageHeader title={t('Users')}>
{seatsCap && seatsCap.maxActiveUsers < Number.POSITIVE_INFINITY ? (
<UserPageHeaderContentWithSeatsCap isSeatsCapExceeded={isSeatsCapExceeded} {...seatsCap} />
) : (
<ButtonGroup>
{canBulkCreateUser && (
<Button icon='mail' onClick={() => router.navigate('/admin/users/invite')} disabled={isSeatsCapExceeded}>
{t('Invite')}
</Button>
)}
{canCreateUser && (
<Button icon='user-plus' onClick={() => router.navigate('/admin/users/new')} disabled={isSeatsCapExceeded}>
{t('New_user')}
</Button>
)}
</ButtonGroup>
)}
<UsersPageHeaderContent isSeatsCapExceeded={isSeatsCapExceeded} seatsCap={seatsCap} />
</PageHeader>
{preventAction?.includes('activeUsers') && (
<Callout type='danger' title={t('subscription.callout.servicesDisruptionsOccurring')} mbe={19} mi={24}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ const SeatsCapUsage = ({ limit, members }: SeatsCapUsageProps): ReactElement =>
const percentage = Math.max(0, Math.min((100 / limit) * members, 100));
const seatsLeft = Math.max(0, limit - members);

return <GenericResourceUsage title={t('Seats_Available', { seatsLeft })} value={members} max={limit} percentage={percentage} />;
return (
<GenericResourceUsage
title={t('Seats_Available', { seatsLeft })}
value={members}
max={limit}
percentage={percentage}
data-testid='seats-cap-progress-bar'
/>
);
};

export default SeatsCapUsage;

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { render, screen } from '@testing-library/react';
import React from 'react';
import '@testing-library/jest-dom';

import UsersPageHeaderContent from './UsersPageHeaderContent';

it('should render "Associate Extension" button when VoIP_TeamCollab_Enabled setting is enabled', async () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().withSetting('VoIP_TeamCollab_Enabled', true).build(),
});

expect(screen.getByRole('button', { name: 'Assign_extension' })).toBeEnabled();
});

it('should not render "Associate Extension" button when VoIP_TeamCollab_Enabled setting is disabled', async () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().withSetting('VoIP_TeamCollab_Enabled', false).build(),
});

expect(screen.queryByRole('button', { name: 'Assign_extension' })).not.toBeInTheDocument();
});

it('should show "Invite" button if has build-register-user permission', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().withPermission('bulk-register-user').build(),
});

expect(screen.getByRole('button', { name: 'Invite' })).toBeInTheDocument();
});

it('should hide "Invite" button if user doesnt have build-register-user permission', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().build(),
});

expect(screen.queryByRole('button', { name: 'Invite' })).not.toBeInTheDocument();
});

it('should show "New User" button if has create-user permission', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().withPermission('create-user').build(),
});

expect(screen.getByRole('button', { name: 'New_user' })).toBeInTheDocument();
});

it('should hide "New User" button if user doesnt have create-user permission', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().withPermission('create-user').build(),
});

expect(screen.getByRole('button', { name: 'New_user' })).toBeInTheDocument();
});

it('should show "Buy more seats" button if seats caps is exceeded', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().build(),
});

expect(screen.getByRole('link', { name: 'Buy_more_seats' })).toBeInTheDocument();
});

it('should hide "Buy more seats" button if seats caps is within limits', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 1 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().build(),
});

expect(screen.queryByRole('link', { name: 'Buy_more_seats' })).not.toBeInTheDocument();
});

it('should show seats available progress bar', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: 10 }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().build(),
});

expect(screen.getByTestId('seats-cap-progress-bar')).toBeInTheDocument();
});

it('should hide seats available progress bar if theres no limit', () => {
render(<UsersPageHeaderContent isSeatsCapExceeded={false} seatsCap={{ activeUsers: 1, maxActiveUsers: Number.POSITIVE_INFINITY }} />, {
legacyRoot: true,
wrapper: mockAppRoot().withJohnDoe().build(),
});

expect(screen.queryByTestId('seats-cap-progress-bar')).not.toBeInTheDocument();
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import { Button, ButtonGroup, Margins } from '@rocket.chat/fuselage';
import { useSetModal, useTranslation, useRouter, useSetting } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { usePermission, useRouter, useSetModal, useSetting } from '@rocket.chat/ui-contexts';
import React from 'react';
import { useTranslation } from 'react-i18next';

import { useExternalLink } from '../../../hooks/useExternalLink';
import { useCheckoutUrl } from '../subscription/hooks/useCheckoutUrl';
import SeatsCapUsage from './SeatsCapUsage';
import type { SeatCapProps } from './useSeatsCap';
import AssignExtensionModal from './voip/AssignExtensionModal';

type UserPageHeaderContentWithSeatsCapProps = {
activeUsers: number;
maxActiveUsers: number;
type UsersPageHeaderContentProps = {
isSeatsCapExceeded: boolean;
seatsCap?: Omit<SeatCapProps, 'reload'>;
};

const UserPageHeaderContentWithSeatsCap = ({
isSeatsCapExceeded,
activeUsers,
maxActiveUsers,
}: UserPageHeaderContentWithSeatsCapProps): ReactElement => {
const t = useTranslation();
const UsersPageHeaderContent = ({ isSeatsCapExceeded, seatsCap }: UsersPageHeaderContentProps) => {
const { t } = useTranslation();
const router = useRouter();
const setModal = useSetModal();

const canCreateUser = usePermission('create-user');
const canBulkCreateUser = usePermission('bulk-register-user');
const canRegisterExtension = useSetting('VoIP_TeamCollab_Enabled');

const manageSubscriptionUrl = useCheckoutUrl()({ target: 'user-page', action: 'buy_more' });
Expand All @@ -38,21 +35,30 @@ const UserPageHeaderContentWithSeatsCap = ({

return (
<>
<Margins inline={16}>
<SeatsCapUsage members={activeUsers} limit={maxActiveUsers} />
</Margins>
{seatsCap && seatsCap.maxActiveUsers < Number.POSITIVE_INFINITY && (
<Margins inline={16}>
<SeatsCapUsage members={seatsCap.activeUsers} limit={seatsCap.maxActiveUsers} />
</Margins>
)}
<ButtonGroup>
{canRegisterExtension && (
<Button icon='phone' onClick={(): void => setModal(<AssignExtensionModal onClose={(): void => setModal(null)} />)}>
{t('Assign_extension')}
</Button>
)}
<Button icon='mail' onClick={handleInviteButtonClick} disabled={isSeatsCapExceeded}>
{t('Invite')}
</Button>
<Button icon='user-plus' onClick={handleNewButtonClick} disabled={isSeatsCapExceeded}>
{t('New_user')}
</Button>

{canBulkCreateUser && (
<Button icon='mail' onClick={handleInviteButtonClick} disabled={isSeatsCapExceeded}>
{t('Invite')}
</Button>
)}

{canCreateUser && (
<Button icon='user-plus' onClick={handleNewButtonClick} disabled={isSeatsCapExceeded}>
{t('New_user')}
</Button>
)}

{isSeatsCapExceeded && (
<Button primary role='link' onClick={() => openExternalLink(manageSubscriptionUrl)}>
{t('Buy_more_seats')}
Expand All @@ -63,4 +69,4 @@ const UserPageHeaderContentWithSeatsCap = ({
);
};

export default UserPageHeaderContentWithSeatsCap;
export default UsersPageHeaderContent;

0 comments on commit 72c6101

Please sign in to comment.