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: User Panel > Tab PENDING #30296

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
72bacc5
feat: :sparkles: Filter users list by active users
rique223 Aug 31, 2023
444c930
feat: :sparkles: Implement new roles filter in the admin users page
rique223 Sep 5, 2023
a98e659
create filter for pending users (work in progress)
felipe-rod123 Sep 6, 2023
d70d1b8
create new pendingAction collumn
felipe-rod123 Sep 11, 2023
089c669
add pending actions count
felipe-rod123 Sep 11, 2023
a1d01d5
WIP: Remove filters
rique223 Sep 12, 2023
91046ad
Merge branch 'feat/user-panel' into feat/active-tab
rique223 Sep 18, 2023
d3f01f7
feat: :sparkles: WIP: Implement new actions menu for users page table
rique223 Sep 21, 2023
c89a193
Merge branch 'feat/active-tab' into WM-164-tab-pending
felipe-rod123 Sep 22, 2023
3682720
fix i18n
felipe-rod123 Sep 22, 2023
26940ba
feat: :sparkles: Finish users table menu
rique223 Sep 25, 2023
58dd010
Merge branch 'feat/active-tab' into WM-164-tab-pending
felipe-rod123 Sep 26, 2023
444ab6f
refactor: :recycle: Remove status badge from contextual bar and reorg
rique223 Sep 26, 2023
6e2f2ad
Typecheck
rique223 Sep 26, 2023
55810be
Re-add icon back to InfoPanelTitle
rique223 Sep 26, 2023
b91dce2
create menu component
felipe-rod123 Sep 27, 2023
c368d06
Merge branch 'feat/user-panel' into feat/active-tab
rique223 Sep 27, 2023
f49a37d
fix spacing
felipe-rod123 Sep 27, 2023
270a38e
fix table rows
felipe-rod123 Sep 27, 2023
96d0c47
fix button
felipe-rod123 Sep 27, 2023
d841d0e
fix review
felipe-rod123 Sep 28, 2023
1b938f8
Merge branch 'feat/active-tab' into WM-164-tab-pending
rique223 Sep 29, 2023
32a4e91
fix: :bug: Implement Activate button and small fixes
rique223 Sep 29, 2023
9d12a02
fix pending actions counter (work in progress)
felipe-rod123 Oct 3, 2023
68d8b7d
fix filters (work in progress)
felipe-rod123 Oct 3, 2023
9f77840
Merge branch 'feat/user-panel' into WM-164-tab-pending
felipe-rod123 Oct 10, 2023
8cf407a
fix lint
felipe-rod123 Oct 10, 2023
cd7e702
fix lint
felipe-rod123 Oct 10, 2023
bc437dd
fix
felipe-rod123 Oct 10, 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
6 changes: 3 additions & 3 deletions apps/meteor/client/components/InfoPanel/InfoPanelTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import React from 'react';

type InfoPanelTitleProps = {
title: string;
icon: ReactNode;
icon?: ReactNode;
};

const isValidIcon = (icon: ReactNode): icon is IconName => typeof icon === 'string';

const InfoPanelTitle: FC<InfoPanelTitleProps> = ({ title, icon }) => (
<Box display='flex' flexShrink={0} alignItems='center' fontScale='h4' color='default' withTruncatedText>
<Box display='flex' flexShrink={0} alignItems='center' fontScale='p1m' color='default' withTruncatedText>
{isValidIcon(icon) ? <Icon name={icon} size='x22' /> : icon}
<Box mis={8} withTruncatedText title={title}>
<Box withTruncatedText title={title}>
{title}
</Box>
</Box>
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/components/UserCard/UserCardRoles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import UserCardInfo from './UserCardInfo';

const UserCardRoles = ({ children }: { children: ReactNode }): ReactElement => (
<Box m='neg-x2'>
<UserCardInfo flexWrap='wrap' display='flex' flexShrink={0}>
<UserCardInfo mb={8} flexWrap='wrap' display='flex' flexShrink={0}>
{children}
</UserCardInfo>
</Box>
Expand Down
48 changes: 24 additions & 24 deletions apps/meteor/client/components/UserInfo/UserInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const UserInfo = ({
email,
verified,
createdAt,
status,
// status,
statusText,
customFields,
canViewAllInfo,
Expand All @@ -78,26 +78,26 @@ const UserInfo = ({
{actions && <InfoPanel.Section>{actions}</InfoPanel.Section>}

<InfoPanel.Section>
{userDisplayName && <InfoPanel.Title icon={status} title={userDisplayName} />}
{userDisplayName && <InfoPanel.Title title={userDisplayName} />}
{statusText && (
<InfoPanel.Text>
<MarkdownText content={statusText} parseEmoji={true} />
<MarkdownText content={statusText} parseEmoji={true} variant='inline' />
</InfoPanel.Text>
)}
</InfoPanel.Section>

<InfoPanel.Section>
{roles.length !== 0 && (
{nickname && (
<InfoPanel.Field>
<InfoPanel.Label>{t('Roles')}</InfoPanel.Label>
<UserCard.Roles>{roles}</UserCard.Roles>
<InfoPanel.Label>{t('Nickname')}</InfoPanel.Label>
<InfoPanel.Text>{nickname}</InfoPanel.Text>
</InfoPanel.Field>
)}

{Number.isInteger(utcOffset) && (
{roles.length !== 0 && (
<InfoPanel.Field>
<InfoPanel.Label>{t('Local_Time')}</InfoPanel.Label>
<InfoPanel.Text>{utcOffset && <UTCClock utcOffset={utcOffset} />}</InfoPanel.Text>
<InfoPanel.Label>{t('Roles')}</InfoPanel.Label>
<UserCard.Roles>{roles}</UserCard.Roles>
</InfoPanel.Field>
)}

Expand All @@ -108,35 +108,35 @@ const UserInfo = ({
</InfoPanel.Field>
)}

{canViewAllInfo && (
{Number.isInteger(utcOffset) && (
<InfoPanel.Field>
<InfoPanel.Label>{t('Last_login')}</InfoPanel.Label>
<InfoPanel.Text>{lastLogin ? timeAgo(lastLogin) : t('Never')}</InfoPanel.Text>
<InfoPanel.Label>{t('Local_Time')}</InfoPanel.Label>
<InfoPanel.Text>{utcOffset && <UTCClock utcOffset={utcOffset} />}</InfoPanel.Text>
</InfoPanel.Field>
)}

{name && (
{bio && (
<InfoPanel.Field>
<InfoPanel.Label>{t('Full_Name')}</InfoPanel.Label>
<InfoPanel.Text>{name}</InfoPanel.Text>
<InfoPanel.Label>{t('Bio')}</InfoPanel.Label>
<InfoPanel.Text withTruncatedText={false}>
<MarkdownText variant='inline' content={bio} />
</InfoPanel.Text>
</InfoPanel.Field>
)}

{nickname && (
{canViewAllInfo && (
<InfoPanel.Field>
<InfoPanel.Label>{t('Nickname')}</InfoPanel.Label>
<InfoPanel.Text>{nickname}</InfoPanel.Text>
<InfoPanel.Label>{t('Last_login')}</InfoPanel.Label>
<InfoPanel.Text>{lastLogin ? timeAgo(lastLogin) : t('Never')}</InfoPanel.Text>
</InfoPanel.Field>
)}

{bio && (
{/* {name && (
<InfoPanel.Field>
<InfoPanel.Label>{t('Bio')}</InfoPanel.Label>
<InfoPanel.Text withTruncatedText={false}>
<MarkdownText variant='inline' content={bio} />
</InfoPanel.Text>
<InfoPanel.Label>{t('Full_Name')}</InfoPanel.Label>
<InfoPanel.Text>{name}</InfoPanel.Text>
</InfoPanel.Field>
)}
)} */}

{phone && (
<InfoPanel.Field>
Expand Down
10 changes: 8 additions & 2 deletions apps/meteor/client/views/admin/users/AdminUserInfoActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ const AdminUserInfoActions = ({
...(changeAdminStatusAction && !isFederatedUser && { makeAdmin: changeAdminStatusAction }),
...(resetE2EKeyAction && !isFederatedUser && { resetE2EKey: resetE2EKeyAction }),
...(resetTOTPAction && !isFederatedUser && { resetTOTP: resetTOTPAction }),
...(deleteUserAction && { delete: deleteUserAction }),
...(changeUserStatusAction && !isFederatedUser && { changeActiveStatus: changeUserStatusAction }),
...(deleteUserAction && { delete: deleteUserAction }),
}),
[
t,
Expand Down Expand Up @@ -116,7 +116,13 @@ const AdminUserInfoActions = ({
secondary
flexShrink={0}
key='menu'
renderItem={({ label: { label, icon }, ...props }): ReactElement => <Option label={label} title={label} icon={icon} {...props} />}
renderItem={({ label: { label, icon }, ...props }): ReactElement =>
label === 'Delete' ? (
<Option label={label} title={label} icon={icon} variant='danger' {...props} />
) : (
<Option label={label} title={label} icon={icon} {...props} />
)
}
options={menuOptions}
/>
);
Expand Down
26 changes: 14 additions & 12 deletions apps/meteor/client/views/admin/users/AdminUsersPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
const canCreateUser = usePermission('create-user');
const canBulkCreateUser = usePermission('bulk-register-user');

const [tab, setTab] = useState<string>('all');
const [tab, setTab] = useState<'all' | 'invited' | 'pending' | 'active' | 'deactivated'>('all');

useEffect(() => {
if (!context || !seatsCap) {
Expand All @@ -42,6 +42,8 @@
reload.current();
};

const [pendingActionsCount, setPendingActionsCount] = useState<number>(0);

Check failure on line 45 in apps/meteor/client/views/admin/users/AdminUsersPage.tsx

View workflow job for this annotation

GitHub Actions / 🔎 Code Check / Code Lint

'setPendingActionsCount' is assigned a value but never used

return (
<Page flexDirection='row'>
<Page>
Expand All @@ -51,7 +53,7 @@
) : (
<ButtonGroup>
{canCreateUser && (
<Button icon='user-plus' onClick={() => router.navigate('/admin/users/new')}>
<Button icon='user-plus' onClick={() => router.navigate('/admin/users/pending')}>
{t('New')}
</Button>
)}
Expand All @@ -68,20 +70,20 @@
<Tabs.Item selected={!tab || tab === 'all'} onClick={() => setTab('all')}>
{t('All')}
</Tabs.Item>
<Tabs.Item selected={tab === 'tab-invited'} onClick={() => setTab('tab-invited')}>
{t('Invited')}
</Tabs.Item>
<Tabs.Item selected={tab === 'tab-new'} onClick={() => setTab('tab-new')}>
{t('New_users')}
<Tabs.Item selected={tab === 'pending'} onClick={() => setTab('pending')}>
{pendingActionsCount === 0 ? t('Pending') : `${t('Pending')} (${pendingActionsCount})`}
Copy link
Contributor

Choose a reason for hiding this comment

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

pendingActionsCount is defined and used but since the set is never ran the number will never change. Why is it incomplete?

</Tabs.Item>
<Tabs.Item selected={tab === 'tab-active'} onClick={() => setTab('tab-active')}>
<Tabs.Item selected={tab === 'active'} onClick={() => setTab('active')}>
{t('Active')}
</Tabs.Item>
<Tabs.Item selected={tab === 'tab-deactivated'} onClick={() => setTab('tab-deactivated')}>
<Tabs.Item selected={tab === 'deactivated'} onClick={() => setTab('deactivated')}>
{t('Deactivated')}
</Tabs.Item>
<Tabs.Item selected={tab === 'invited'} onClick={() => setTab('invited')}>
{t('Invited')}
</Tabs.Item>
</Tabs>
<UsersTable reload={reload} />
<UsersTable reload={reload} tab={tab} onReload={handleReload} />
</Page.Content>
</Page>
{context && (
Expand All @@ -90,14 +92,14 @@
<ContextualbarTitle>
{context === 'info' && t('User_Info')}
{context === 'edit' && t('Edit_User')}
{context === 'new' && t('Add_User')}
{context === 'pending' && t('Add_User')}
{context === 'invite' && t('Invite_Users')}
</ContextualbarTitle>
<ContextualbarClose onClick={() => router.navigate('/admin/users')} />
</ContextualbarHeader>
{context === 'info' && id && <AdminUserInfoWithData uid={id} onReload={handleReload} />}
{context === 'edit' && id && <AdminUserFormWithData uid={id} onReload={handleReload} />}
{context === 'new' && <AdminUserForm onReload={handleReload} />}
{context === 'pending' && <AdminUserForm onReload={handleReload} />}
{context === 'invite' && <AdminInviteUsers />}
</Contextualbar>
)}
Expand Down
106 changes: 106 additions & 0 deletions apps/meteor/client/views/admin/users/UsersTable/ActionsMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import type { IUser } from '@rocket.chat/core-typings';
import { isUserFederated } from '@rocket.chat/core-typings';
import { Menu, Option } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import type { useQuery } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import React, { useMemo } from 'react';

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 ActionsMenuProps = {
user: Pick<IUser, '_id' | 'username' | 'name' | 'status' | 'emails' | 'active' | 'avatarETag' | 'roles'>;
refetchUsers: ReturnType<typeof useQuery>['refetch'];
onReload: () => void;
tab: string;
};

const ActionsMenu = ({ user, refetchUsers, onReload, tab }: ActionsMenuProps): ReactElement | null => {
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 activeMenuOptions = useMemo(
() =>
tab === 'active'
? {
...(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 },
}),
}
: {},
[changeAdminStatusAction, isFederatedUser, resetE2EKeyAction, resetTOTPAction, tab],
);

const menuOptions = useMemo(
() => ({
...activeMenuOptions,
...(changeUserStatusAction &&
!isFederatedUser && {
changeActiveStatus: {
label: { label: changeUserStatusAction.label, icon: changeUserStatusAction.icon },
action: changeUserStatusAction.action,
},
}),
...(deleteUserAction && {
delete: { label: { label: deleteUserAction.label, icon: deleteUserAction.icon }, action: deleteUserAction.action },
}),
}),
[activeMenuOptions, changeUserStatusAction, deleteUserAction, isFederatedUser],
);

return useMemo(() => {
if (!menuOptions) {
return null;
}

return (
<Menu
mi={4}
placement='bottom-start'
flexShrink={0}
key='menu'
renderItem={({ label: { label, icon }, ...props }): ReactElement =>
label === 'Delete' ? (
<Option label={label} title={label} icon={icon} variant='danger' {...props} />
) : (
<Option label={label} title={label} icon={icon} {...props} />
)
}
options={menuOptions}
aria-keyshortcuts='alt'
tabIndex={-1}
/>
);
}, [menuOptions]);
};

export default ActionsMenu;
Loading
Loading