Skip to content

Commit

Permalink
feat: ✨ WIP: Implement new actions menu for users page table
Browse files Browse the repository at this point in the history
  • Loading branch information
rique223 committed Sep 21, 2023
1 parent 91046ad commit d3f01f7
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 12 deletions.
2 changes: 1 addition & 1 deletion apps/meteor/client/views/admin/users/AdminUsersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const UsersPage = (): ReactElement => {
{t('Deactivated')}
</Tabs.Item>
</Tabs>
<UsersTable reload={reload} tab={tab} />
<UsersTable reload={reload} tab={tab} onReload={handleReload} />
</Page.Content>
</Page>
{context && (
Expand Down
21 changes: 16 additions & 5 deletions apps/meteor/client/views/admin/users/UsersTable/UsersTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import UsersTableRow from './UsersTableRow';
type UsersTableProps = {
reload: MutableRefObject<() => void>;
tab: string;
onReload: () => void;
};

// TODO: Missing error state
const UsersTable = ({ reload, tab }: UsersTableProps): ReactElement | null => {
const UsersTable = ({ reload, tab, onReload }: UsersTableProps): ReactElement | null => {
const t = useTranslation();
const router = useRouter();
const mediaQuery = useMediaQuery('(min-width: 1024px)');
Expand Down Expand Up @@ -57,15 +58,17 @@ const UsersTable = ({ reload, tab }: UsersTableProps): ReactElement | null => {
prevSearchTerm.current = searchTerm;
}, [reload, refetch, searchTerm]);

const handleClick = useMutableCallback((id): void =>
const handleClick = useMutableCallback((id, e: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>): void => {
e.stopPropagation();

router.navigate({
name: 'admin-users',
params: {
context: 'info',
id,
},
}),
);
});
});

const headers = useMemo(
() => [
Expand Down Expand Up @@ -102,6 +105,7 @@ const UsersTable = ({ reload, tab }: UsersTableProps): ReactElement | null => {
<GenericTableHeaderCell w='x100' key='status' direction={sortDirection} active={sortBy === 'status'} onClick={setSort} sort='status'>
{t('Registration_status')}
</GenericTableHeaderCell>,
<GenericTableHeaderCell key='actions' w='x44' />,
],
[mediaQuery, setSort, sortBy, sortDirection, t],
);
Expand All @@ -121,7 +125,14 @@ const UsersTable = ({ reload, tab }: UsersTableProps): ReactElement | null => {
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
{filteredUsers.map((user) => (
<UsersTableRow key={user._id} onClick={handleClick} mediaQuery={mediaQuery} user={user} />
<UsersTableRow
key={user._id}
onClick={handleClick}
mediaQuery={mediaQuery}
user={user}
refetchUsers={refetch}
onReload={onReload}
/>
))}
</GenericTableBody>
</GenericTable>
Expand Down
75 changes: 69 additions & 6 deletions apps/meteor/client/views/admin/users/UsersTable/UsersTableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import { UserStatus as Status } from '@rocket.chat/core-typings';
import { UserStatus as Status, isUserFederated } from '@rocket.chat/core-typings';
import type { IRole, IUser } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import { Box, Menu, Option } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { useQuery } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import React from 'react';

import { Roles } from '../../../../../app/models/client';
import { GenericTableRow, GenericTableCell } from '../../../../components/GenericTable';
import { UserStatus } from '../../../../components/UserStatus';
import UserAvatar from '../../../../components/avatar/UserAvatar';
import { useChangeAdminStatusAction } from '../hooks/useChangeAdminStatusAction';
import { useChangeUserStatusAction } from '../hooks/useChangeUserStatusAction';
import { useDeleteUserAction } from '../hooks/useDeleteUserAction';
import { useResetE2EEKeyAction } from '../hooks/useResetE2EEKeyAction';
import { useResetTOTPAction } from '../hooks/useResetTOTPAction';

type UsersTableRowProps = {
user: Pick<IUser, '_id' | 'username' | 'name' | 'status' | 'emails' | 'active' | 'avatarETag' | 'roles'>;
onClick: (id: IUser['_id']) => void;
onClick: (id: IUser['_id'], e: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => void;
mediaQuery: boolean;
refetchUsers: ReturnType<typeof useQuery>['refetch'];
onReload: () => void;
};

const UsersTableRow = ({ user, onClick, mediaQuery }: UsersTableRowProps): ReactElement => {
const UsersTableRow = ({ user, onClick, mediaQuery, refetchUsers, onReload }: UsersTableRowProps): ReactElement => {
const t = useTranslation();
const { _id, emails, username, name, status, roles, active, avatarETag } = user;
const registrationStatusText = active ? t('Active') : t('Deactivated');
Expand All @@ -26,10 +35,54 @@ const UsersTableRow = ({ user, onClick, mediaQuery }: UsersTableRowProps): React
.filter((roleName): roleName is string => !!roleName)
.join(', ');

const userId = user._id;
const isAdmin = user.roles?.includes('admin');
const isActive = user.active;
const isFederatedUser = isUserFederated(user);

const onChange = useMutableCallback(() => {
onReload();
refetchUsers();
});

const changeAdminStatusAction = useChangeAdminStatusAction(userId, isAdmin, onChange);
const changeUserStatusAction = useChangeUserStatusAction(userId, isActive, onChange);
const deleteUserAction = useDeleteUserAction(userId, onChange, onReload);
const resetTOTPAction = useResetTOTPAction(userId);
const resetE2EKeyAction = useResetE2EEKeyAction(userId);

const menuOptions = {
...(changeAdminStatusAction &&
!isFederatedUser && {
makeAdmin: {
label: { label: changeAdminStatusAction.label, icon: changeAdminStatusAction.icon },
action: changeAdminStatusAction.action,
},
}),
...(resetE2EKeyAction &&
!isFederatedUser && {
resetE2EKey: { label: { label: resetE2EKeyAction.label, icon: resetE2EKeyAction.icon }, action: resetE2EKeyAction.action },
}),
...(resetTOTPAction &&
!isFederatedUser && {
resetTOTP: { label: { label: resetTOTPAction.label, icon: resetTOTPAction.icon }, action: resetTOTPAction.action },
}),
...(deleteUserAction && {
delete: { label: { label: deleteUserAction.label, icon: deleteUserAction.icon }, action: deleteUserAction.action },
}),
...(changeUserStatusAction &&
!isFederatedUser && {
changeActiveStatus: {
label: { label: changeUserStatusAction.label, icon: changeUserStatusAction.icon },
action: changeUserStatusAction.action,
},
}),
};

return (
<GenericTableRow
onKeyDown={(): void => onClick(_id)}
onClick={(): void => onClick(_id)}
onKeyDown={(e): void => onClick(_id, e)}
onClick={(e): void => onClick(_id, e)}
tabIndex={0}
role='link'
action
Expand Down Expand Up @@ -69,6 +122,16 @@ const UsersTableRow = ({ user, onClick, mediaQuery }: UsersTableRowProps): React
<GenericTableCell fontScale='p2' color='hint' withTruncatedText>
{registrationStatusText}
</GenericTableCell>
<GenericTableCell>
<Menu
mi={4}
placement='bottom-start'
flexShrink={0}
key='menu'
renderItem={({ label: { label, icon }, ...props }): ReactElement => <Option label={label} title={label} icon={icon} {...props} />}
options={menuOptions}
/>
</GenericTableCell>
</GenericTableRow>
);
};
Expand Down

0 comments on commit d3f01f7

Please sign in to comment.