Skip to content
Snippets Groups Projects
Unverified Commit decefab5 authored by Douglas Fabris's avatar Douglas Fabris Committed by GitHub
Browse files

Chore: Refactor Directory Tables (#27646)

parent 973a1d90
No related branches found
No related tags found
No related merge requests found
Showing
with 427 additions and 332 deletions
......@@ -11,14 +11,15 @@ export const RoomIcon = ({
room,
size = 'x16',
isIncomingCall,
placement,
placement = 'default',
}: {
room: IRoom;
size?: ComponentProps<typeof Icon>['size'];
isIncomingCall?: boolean;
placement: 'sidebar' | 'default';
placement?: 'sidebar' | 'default';
}): ReactElement | null => {
const iconPropsOrReactNode = useRoomIcon(room);
if (isIncomingCall) {
return <Icon name='phone' size={size} />;
}
......
import { Box, Table, Avatar, Icon } from '@rocket.chat/fuselage';
import { useMediaQuery, useAutoFocus } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo, useState, useCallback } from 'react';
import FilterByText from '../../components/FilterByText';
import GenericTable from '../../components/GenericTable';
import MarkdownText from '../../components/MarkdownText';
import { useEndpointData } from '../../hooks/useEndpointData';
import { useFormatDate } from '../../hooks/useFormatDate';
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
import RoomTags from './RoomTags';
import { useDirectoryQuery } from './hooks/useDirectoryQuery';
const style = {
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
};
function ChannelsTable() {
const t = useTranslation();
const refAutoFocus = useAutoFocus(true);
const [sort, setSort] = useState(['name', 'asc']);
const [params, setParams] = useState({ current: 0, itemsPerPage: 25 });
const mediaQuery = useMediaQuery('(min-width: 768px)');
const query = useDirectoryQuery(params, sort, 'channels');
const onHeaderClick = useCallback(
(id) => {
const [sortBy, sortDirection] = sort;
if (sortBy === id) {
setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
return;
}
setSort([id, 'asc']);
},
[sort],
);
const header = useMemo(
() =>
[
<GenericTable.HeaderCell key={'name'} direction={sort[1]} active={sort[0] === 'name'} onClick={onHeaderClick} sort='name'>
{t('Name')}
</GenericTable.HeaderCell>,
<GenericTable.HeaderCell
key={'usersCount'}
direction={sort[1]}
active={sort[0] === 'usersCount'}
onClick={onHeaderClick}
sort='usersCount'
style={{ width: '100px' }}
>
{t('Users')}
</GenericTable.HeaderCell>,
mediaQuery && (
<GenericTable.HeaderCell
key={'createdAt'}
direction={sort[1]}
active={sort[0] === 'createdAt'}
onClick={onHeaderClick}
sort='createdAt'
style={{ width: '150px' }}
>
{t('Created_at')}
</GenericTable.HeaderCell>
),
mediaQuery && (
<GenericTable.HeaderCell
key={'lastMessage'}
direction={sort[1]}
active={sort[0] === 'lastMessage'}
onClick={onHeaderClick}
sort='lastMessage'
style={{ width: '150px' }}
>
{t('Last_Message')}
</GenericTable.HeaderCell>
),
mediaQuery && (
<GenericTable.HeaderCell key={'belongsTo'} style={{ width: '150px' }}>
{t('Belongs_To')}
</GenericTable.HeaderCell>
),
].filter(Boolean),
[sort, onHeaderClick, t, mediaQuery],
);
const channelRoute = useRoute('channel');
const groupsRoute = useRoute('group');
const { value: data = {} } = useEndpointData('/v1/directory', { params: query });
const onClick = useMemo(
() => (name, type) => (e) => {
if (e.type === 'click' || e.key === 'Enter') {
type === 'c' ? channelRoute.push({ name }) : groupsRoute.push({ name });
}
},
[channelRoute, groupsRoute],
);
const formatDate = useFormatDate();
const renderRow = useCallback(
(room) => {
const { _id, ts, t, name, fname, usersCount, lastMessage, topic, belongsTo } = room;
const avatarUrl = roomCoordinator.getRoomDirectives(t)?.getAvatarPath(room);
return (
<Table.Row key={_id} onKeyDown={onClick(name, t)} onClick={onClick(name, t)} tabIndex={0} role='link' action>
<Table.Cell>
<Box display='flex'>
<Box flexGrow={0}>
<Avatar size='x40' title={fname || name} url={avatarUrl} />
</Box>
<Box grow={1} mi='x8' style={style}>
<Box display='flex' alignItems='center'>
<Icon name={roomCoordinator.getIcon(room)} color='hint' />{' '}
<Box fontScale='p2m' mi='x4'>
{fname || name}
</Box>
<RoomTags room={room} style={style} />
</Box>
{topic && <MarkdownText variant='inlineWithoutBreaks' fontScale='p2' color='hint' style={style} content={topic} />}
</Box>
</Box>
</Table.Cell>
<Table.Cell fontScale='p2' color='hint' style={style}>
{usersCount}
</Table.Cell>
{mediaQuery && (
<Table.Cell fontScale='p2' color='hint' style={style}>
{formatDate(ts)}
</Table.Cell>
)}
{mediaQuery && (
<Table.Cell fontScale='p2' color='hint' style={style}>
{lastMessage && formatDate(lastMessage.ts)}
</Table.Cell>
)}
{mediaQuery && (
<Table.Cell fontScale='p2' color='hint' style={style}>
{belongsTo}
</Table.Cell>
)}
</Table.Row>
);
},
[formatDate, mediaQuery, onClick],
);
return (
<GenericTable
header={header}
renderFilter={({ onChange, ...props }) => (
<FilterByText placeholder={t('Search_Channels')} inputRef={refAutoFocus} onChange={onChange} {...props} />
)}
renderRow={renderRow}
results={data.result}
setParams={setParams}
total={data.total}
/>
);
}
export default ChannelsTable;
......@@ -4,9 +4,9 @@ import type { ReactElement } from 'react';
import React, { useEffect, useCallback } from 'react';
import Page from '../../components/Page';
import ChannelsTab from './ChannelsTab';
import TeamsTab from './TeamsTab';
import UsersTab from './UsersTab';
import ChannelsTab from './tabs/channels/ChannelsTab';
import TeamsTab from './tabs/teams/TeamsTab';
import UsersTab from './tabs/users/UsersTab';
const DirectoryPage = (): ReactElement => {
const t = useTranslation();
......
......@@ -4,16 +4,17 @@ import { useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';
function RoomTags({ room }: { room: IRoom }): ReactElement {
const RoomTags = ({ room }: { room: IRoom }): ReactElement => {
const t = useTranslation();
return (
<Box mi='x4' alignItems='center' display='flex'>
<Box mi='x4' alignItems='center' display='flex' withTruncatedText>
<Margins inline='x2'>
{room.default && <Tag variant='secondary'>{t('default')}</Tag>}
{room.featured && <Tag variant='secondary'>{t('featured')}</Tag>}
</Margins>
</Box>
);
}
};
export default RoomTags;
import { Box, Table, Avatar, Icon } from '@rocket.chat/fuselage';
import { useAutoFocus, useMediaQuery } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo, useState, useCallback } from 'react';
import FilterByText from '../../components/FilterByText';
import GenericTable from '../../components/GenericTable';
import MarkdownText from '../../components/MarkdownText';
import { useEndpointData } from '../../hooks/useEndpointData';
import { useFormatDate } from '../../hooks/useFormatDate';
import { roomCoordinator } from '../../lib/rooms/roomCoordinator';
import RoomTags from './RoomTags';
import { useDirectoryQuery } from './hooks/useDirectoryQuery';
const style = {
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
};
function TeamsTable() {
const t = useTranslation();
const [sort, setSort] = useState(['name', 'asc']);
const [params, setParams] = useState({ current: 0, itemsPerPage: 25 });
const refAutoFocus = useAutoFocus(true);
const mediaQuery = useMediaQuery('(min-width: 768px)');
const onHeaderClick = useCallback(
(id) => {
const [sortBy, sortDirection] = sort;
if (sortBy === id) {
setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
return;
}
setSort([id, 'asc']);
},
[sort],
);
const header = useMemo(
() =>
[
<GenericTable.HeaderCell key={'name'} direction={sort[1]} active={sort[0] === 'name'} onClick={onHeaderClick} sort='name'>
{t('Name')}
</GenericTable.HeaderCell>,
<GenericTable.HeaderCell key={'channelsCount'} style={{ width: '100px' }}>
{t('Channels')}
</GenericTable.HeaderCell>,
mediaQuery && (
<GenericTable.HeaderCell
key={'createdAt'}
direction={sort[1]}
active={sort[0] === 'createdAt'}
onClick={onHeaderClick}
sort='createdAt'
style={{ width: '150px' }}
>
{t('Created_at')}
</GenericTable.HeaderCell>
),
].filter(Boolean),
[sort, onHeaderClick, t, mediaQuery],
);
const channelsRoute = useRoute('channel');
const groupsRoute = useRoute('group');
const query = useDirectoryQuery(params, sort, 'teams');
const { value: data = {} } = useEndpointData('/v1/directory', { params: query });
const onClick = useMemo(
() => (name, type) => (e) => {
if (e.type === 'click' || e.key === 'Enter') {
type === 'c' ? channelsRoute.push({ name }) : groupsRoute.push({ name });
}
},
[channelsRoute, groupsRoute],
);
const formatDate = useFormatDate();
const renderRow = useCallback(
(team) => {
const { _id, ts, t, name, fname, topic, roomsCount } = team;
const avatarUrl = roomCoordinator.getRoomDirectives(t)?.getAvatarPath(team);
return (
<Table.Row key={_id} onKeyDown={onClick(name, t)} onClick={onClick(name, t)} tabIndex={0} role='link' action>
<Table.Cell>
<Box display='flex'>
<Box flexGrow={0}>
<Avatar size='x40' title={fname || name} url={avatarUrl} />
</Box>
<Box grow={1} mi='x8' style={style}>
<Box display='flex' alignItems='center'>
<Icon name={roomCoordinator.getIcon(team)} color='hint' />{' '}
<Box fontScale='p2m' mi='x4'>
{fname || name}
</Box>
<RoomTags room={team} style={style} />
</Box>
{topic && <MarkdownText variant='inlineWithoutBreaks' fontScale='p2' color='hint' style={style} content={topic} />}
</Box>
</Box>
</Table.Cell>
<Table.Cell fontScale='p2' color='hint' style={style}>
{roomsCount}
</Table.Cell>
{mediaQuery && (
<Table.Cell fontScale='p2' color='hint' style={style}>
{formatDate(ts)}
</Table.Cell>
)}
</Table.Row>
);
},
[formatDate, mediaQuery, onClick],
);
return (
<GenericTable
header={header}
renderFilter={({ onChange, ...props }) => (
<FilterByText placeholder={t('Teams_Search_teams')} inputRef={refAutoFocus} onChange={onChange} {...props} />
)}
renderRow={renderRow}
results={data.result}
setParams={setParams}
total={data.total}
/>
);
}
export default TeamsTable;
......@@ -2,10 +2,10 @@ import { usePermission } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';
import NotAuthorizedPage from '../notAuthorized/NotAuthorizedPage';
import NotAuthorizedPage from '../../../notAuthorized/NotAuthorizedPage';
import ChannelsTable from './ChannelsTable';
function ChannelsTab(): ReactElement {
const ChannelsTab = (): ReactElement => {
const canViewPublicRooms = usePermission('view-c-room');
if (canViewPublicRooms) {
......@@ -13,6 +13,6 @@ function ChannelsTab(): ReactElement {
}
return <NotAuthorizedPage />;
}
};
export default ChannelsTab;
import type { IRoom } from '@rocket.chat/core-typings';
import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage';
import { useDebouncedValue, useMediaQuery } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import React, { useMemo, useState } from 'react';
import FilterByText from '../../../../../components/FilterByText';
import {
GenericTable,
GenericTableHeader,
GenericTableHeaderCell,
GenericTableBody,
GenericTableLoadingTable,
} from '../../../../../components/GenericTable';
import { usePagination } from '../../../../../components/GenericTable/hooks/usePagination';
import { useSort } from '../../../../../components/GenericTable/hooks/useSort';
import { useDirectoryQuery } from '../../../hooks/useDirectoryQuery';
import ChannelsTableRow from './ChannelsTableRow';
const ChannelsTable = () => {
const t = useTranslation();
const mediaQuery = useMediaQuery('(min-width: 768px)');
const [text, setText] = useState('');
const debouncedText = useDebouncedValue(text, 500);
const channelRoute = useRoute('channel');
const groupsRoute = useRoute('group');
const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
const { sortBy, sortDirection, setSort } = useSort<'name' | 'usersCount' | 'lastMessage' | 'createdAt'>('name');
const headers = useMemo(
() =>
[
<GenericTableHeaderCell key='name' direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'>
{t('Name')}
</GenericTableHeaderCell>,
<GenericTableHeaderCell
key='usersCount'
direction={sortDirection}
active={sortBy === 'usersCount'}
onClick={setSort}
sort='usersCount'
w='100px'
>
{t('Users')}
</GenericTableHeaderCell>,
mediaQuery && (
<GenericTableHeaderCell
key='createdAt'
direction={sortDirection}
active={sortBy === 'createdAt'}
onClick={setSort}
sort='createdAt'
w='150px'
>
{t('Created_at')}
</GenericTableHeaderCell>
),
mediaQuery && (
<GenericTableHeaderCell
key='lastMessage'
direction={sortDirection}
active={sortBy === 'lastMessage'}
onClick={setSort}
sort='lastMessage'
w='150px'
>
{t('Last_Message')}
</GenericTableHeaderCell>
),
mediaQuery && (
<GenericTableHeaderCell key='belongsTo' w='150px'>
{t('Belongs_To')}
</GenericTableHeaderCell>
),
].filter(Boolean),
[setSort, sortBy, t, sortDirection, mediaQuery],
);
const getDirectoryData = useEndpoint('GET', '/v1/directory');
const query = useDirectoryQuery({ text: debouncedText, current, itemsPerPage }, [sortBy, sortDirection], 'channels');
const { data, isFetched, isLoading, isError, refetch } = useQuery(['getDirectoryData', query], () => getDirectoryData(query));
const onClick = useMemo(
() => (name: IRoom['name'], type: IRoom['t']) => (e: React.KeyboardEvent | React.MouseEvent) => {
if (name && (e.type === 'click' || (e as React.KeyboardEvent).key === 'Enter')) {
type === 'c' ? channelRoute.push({ name }) : groupsRoute.push({ name });
}
},
[channelRoute, groupsRoute],
);
return (
<>
<FilterByText autoFocus placeholder={t('Search_Channels')} onChange={({ text }): void => setText(text)} />
{isLoading && (
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
<GenericTableLoadingTable headerCells={5} />
</GenericTableBody>
</GenericTable>
)}
{data?.result && data.result.length > 0 && isFetched && (
<>
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
{data.result.map((room) => (
<ChannelsTableRow key={room._id} room={room as unknown as IRoom} onClick={onClick} mediaQuery={mediaQuery} />
))}
</GenericTableBody>
</GenericTable>
<Pagination
divider
current={current}
itemsPerPage={itemsPerPage}
count={data?.total || 0}
onSetItemsPerPage={onSetItemsPerPage}
onSetCurrent={onSetCurrent}
{...paginationProps}
/>
</>
)}
{isFetched && data?.result.length === 0 && (
<States>
<StatesIcon name='magnifier' />
<StatesTitle>{t('No_results_found')}</StatesTitle>
</States>
)}
{isError && (
<States>
<StatesIcon name='warning' variation='danger' />
<StatesTitle>{t('Something_went_wrong')}</StatesTitle>
<StatesActions>
<StatesAction onClick={() => refetch()}>{t('Reload_page')}</StatesAction>
</StatesActions>
</States>
)}
</>
);
};
export default ChannelsTable;
import type { IRoom, ITeam } from '@rocket.chat/core-typings';
import { Box, TableRow, TableCell, Avatar } from '@rocket.chat/fuselage';
import React from 'react';
import MarkdownText from '../../../../../components/MarkdownText';
import { RoomIcon } from '../../../../../components/RoomIcon';
import { useFormatDate } from '../../../../../hooks/useFormatDate';
import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
import RoomTags from '../../../RoomTags';
type ChannelsTableRowProps = {
onClick: (name: IRoom['name'], type: IRoom['t']) => (e: React.KeyboardEvent | React.MouseEvent) => void;
room: IRoom & { belongsTo?: ITeam };
mediaQuery: boolean;
};
const ChannelsTableRow = ({ onClick, room, mediaQuery }: ChannelsTableRowProps) => {
const formatDate = useFormatDate();
const { _id, ts, t, name, fname, usersCount, lastMessage, topic, belongsTo } = room;
const avatarUrl = roomCoordinator.getRoomDirectives(t)?.getAvatarPath(room);
return (
<TableRow key={_id} onKeyDown={onClick(name, t)} onClick={onClick(name, t)} tabIndex={0} role='link' action>
<TableCell>
<Box display='flex'>
<Box flexGrow={0}>{avatarUrl && <Avatar size='x40' title={fname || name} url={avatarUrl} />}</Box>
<Box flexGrow={1} mi='x8' withTruncatedText>
<Box display='flex' alignItems='center'>
<RoomIcon room={room} />
<Box fontScale='p2m' mi='x4'>
{fname || name}
</Box>
<RoomTags room={room} />
</Box>
{topic && <MarkdownText variant='inlineWithoutBreaks' fontScale='p2' color='hint' withTruncatedText content={topic} />}
</Box>
</Box>
</TableCell>
<TableCell fontScale='p2' color='hint' withTruncatedText>
{usersCount}
</TableCell>
{mediaQuery && ts && (
<TableCell fontScale='p2' color='hint' withTruncatedText>
{formatDate(ts)}
</TableCell>
)}
{mediaQuery && (
<TableCell fontScale='p2' color='hint' withTruncatedText>
{lastMessage && formatDate(lastMessage.ts)}
</TableCell>
)}
{mediaQuery && (
<TableCell fontScale='p2' color='hint' withTruncatedText>
{belongsTo}
</TableCell>
)}
</TableRow>
);
};
export default ChannelsTableRow;
export { default } from './ChannelsTable';
......@@ -2,10 +2,10 @@ import { usePermission } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';
import NotAuthorizedPage from '../notAuthorized/NotAuthorizedPage';
import TeamsTable from './TeamsTable';
import NotAuthorizedPage from '../../../notAuthorized/NotAuthorizedPage';
import TeamsTable from './TeamsTable/TeamsTable';
function TeamsTab(): ReactElement {
const TeamsTab = (): ReactElement => {
const canViewPublicRooms = usePermission('view-c-room');
if (canViewPublicRooms) {
......@@ -13,6 +13,6 @@ function TeamsTab(): ReactElement {
}
return <NotAuthorizedPage />;
}
};
export default TeamsTab;
import type { IRoom } from '@rocket.chat/core-typings';
import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage';
import { useMediaQuery, useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import React, { useMemo, useState } from 'react';
import FilterByText from '../../../../../components/FilterByText';
import {
GenericTable,
GenericTableHeader,
GenericTableHeaderCell,
GenericTableBody,
GenericTableLoadingTable,
} from '../../../../../components/GenericTable';
import { usePagination } from '../../../../../components/GenericTable/hooks/usePagination';
import { useSort } from '../../../../../components/GenericTable/hooks/useSort';
import { useDirectoryQuery } from '../../../hooks/useDirectoryQuery';
import TeamsTableRow from './TeamsTableRow';
const TeamsTable = () => {
const t = useTranslation();
const mediaQuery = useMediaQuery('(min-width: 768px)');
const [text, setText] = useState('');
const debouncedText = useDebouncedValue(text, 500);
const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
const { sortBy, sortDirection, setSort } = useSort<'name' | 'usersCount' | 'lastMessage' | 'createdAt'>('name');
const headers = useMemo(
() =>
[
<GenericTableHeaderCell key='name' direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'>
{t('Name')}
</GenericTableHeaderCell>,
<GenericTableHeaderCell key='channelsCount' w='100px'>
{t('Channels')}
</GenericTableHeaderCell>,
mediaQuery && (
<GenericTableHeaderCell
key='createdAt'
direction={sortDirection}
active={sortBy === 'createdAt'}
onClick={setSort}
sort='createdAt'
w='150px'
>
{t('Created_at')}
</GenericTableHeaderCell>
),
].filter(Boolean),
[setSort, sortBy, sortDirection, t, mediaQuery],
);
const channelsRoute = useRoute('channel');
const groupsRoute = useRoute('group');
const getDirectoryData = useEndpoint('GET', '/v1/directory');
const query = useDirectoryQuery({ text: debouncedText, current, itemsPerPage }, [sortBy, sortDirection], 'teams');
const { data, isFetched, isLoading, isError, refetch } = useQuery(['getDirectoryData', query], () => getDirectoryData(query));
const onClick = useMemo(
() => (name: IRoom['name'], type: IRoom['t']) => (e: React.KeyboardEvent | React.MouseEvent) => {
if (name && (e.type === 'click' || (e as React.KeyboardEvent).key === 'Enter')) {
type === 'c' ? channelsRoute.push({ name }) : groupsRoute.push({ name });
}
},
[channelsRoute, groupsRoute],
);
return (
<>
<FilterByText placeholder={t('Teams_Search_teams')} autoFocus onChange={({ text }): void => setText(text)} />
{isLoading && (
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
<GenericTableLoadingTable headerCells={3} />
</GenericTableBody>
</GenericTable>
)}
{data?.result && data.result.length > 0 && isFetched && (
<>
<GenericTable>
<GenericTableHeader>{headers}</GenericTableHeader>
<GenericTableBody>
{data.result.map((team) => (
<TeamsTableRow
key={team._id}
team={team as unknown as IRoom & { roomsCount: number }}
onClick={onClick}
mediaQuery={mediaQuery}
/>
))}
</GenericTableBody>
</GenericTable>
<Pagination
divider
current={current}
itemsPerPage={itemsPerPage}
count={data?.total || 0}
onSetItemsPerPage={onSetItemsPerPage}
onSetCurrent={onSetCurrent}
{...paginationProps}
/>
</>
)}
{isFetched && data?.result.length === 0 && (
<States>
<StatesIcon name='magnifier' />
<StatesTitle>{t('No_results_found')}</StatesTitle>
</States>
)}
{isError && (
<States>
<StatesIcon name='warning' variation='danger' />
<StatesTitle>{t('Something_went_wrong')}</StatesTitle>
<StatesActions>
<StatesAction onClick={() => refetch()}>{t('Reload_page')}</StatesAction>
</StatesActions>
</States>
)}
</>
);
};
export default TeamsTable;
import type { IRoom } from '@rocket.chat/core-typings';
import { Box, TableRow, TableCell, Avatar } from '@rocket.chat/fuselage';
import React from 'react';
import MarkdownText from '../../../../../components/MarkdownText';
import { RoomIcon } from '../../../../../components/RoomIcon';
import { useFormatDate } from '../../../../../hooks/useFormatDate';
import { roomCoordinator } from '../../../../../lib/rooms/roomCoordinator';
import RoomTags from '../../../RoomTags';
type TeamsTableRowProps = {
onClick: (name: IRoom['name'], type: IRoom['t']) => (e: React.KeyboardEvent | React.MouseEvent) => void;
team: IRoom & { roomsCount: number };
mediaQuery: boolean;
};
const TeamsTableRow = ({ onClick, team, mediaQuery }: TeamsTableRowProps) => {
const formatDate = useFormatDate();
const { _id, ts, t, name, fname, topic, roomsCount } = team;
const avatarUrl = roomCoordinator.getRoomDirectives(t)?.getAvatarPath(team);
return (
<TableRow key={_id} onKeyDown={onClick(name, t)} onClick={onClick(name, t)} tabIndex={0} role='link' action>
<TableCell>
<Box display='flex'>
<Box flexGrow={0}>{avatarUrl && <Avatar size='x40' title={fname || name} url={avatarUrl} />}</Box>
<Box flexGrow={1} mi='x8' withTruncatedText>
<Box display='flex' alignItems='center'>
<RoomIcon room={team} />
<Box fontScale='p2m' mi='x4'>
{fname || name}
</Box>
<RoomTags room={team} />
</Box>
{topic && <MarkdownText variant='inlineWithoutBreaks' fontScale='p2' color='hint' withTruncatedText content={topic} />}
</Box>
</Box>
</TableCell>
<TableCell fontScale='p2' color='hint' withTruncatedText>
{roomsCount}
</TableCell>
{mediaQuery && ts && (
<TableCell fontScale='p2' color='hint' withTruncatedText>
{formatDate(ts)}
</TableCell>
)}
</TableRow>
);
};
export default TeamsTableRow;
export { default } from './TeamsTable';
......@@ -2,7 +2,7 @@ import { usePermission } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React from 'react';
import NotAuthorizedPage from '../notAuthorized/NotAuthorizedPage';
import NotAuthorizedPage from '../../../notAuthorized/NotAuthorizedPage';
import UsersTable from './UsersTable';
const UsersTab = (props: { workspace?: 'external' | 'local' }): ReactElement => {
......
import type { IUser, Serialized } from '@rocket.chat/core-typings';
import { Pagination, States, StatesIcon, StatesTitle } from '@rocket.chat/fuselage';
import { Pagination, States, StatesIcon, StatesTitle, StatesActions, StatesAction } from '@rocket.chat/fuselage';
import { useDebouncedValue, useMediaQuery } from '@rocket.chat/fuselage-hooks';
import { usePermission, useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import FilterByText from '../../../components/FilterByText';
import FilterByText from '../../../../../components/FilterByText';
import {
GenericTable,
GenericTableHeader,
GenericTableHeaderCell,
GenericTableBody,
GenericTableLoadingTable,
} from '../../../components/GenericTable';
import { usePagination } from '../../../components/GenericTable/hooks/usePagination';
import { useSort } from '../../../components/GenericTable/hooks/useSort';
import { useDirectoryQuery } from '../hooks/useDirectoryQuery';
} from '../../../../../components/GenericTable';
import { usePagination } from '../../../../../components/GenericTable/hooks/usePagination';
import { useSort } from '../../../../../components/GenericTable/hooks/useSort';
import { useDirectoryQuery } from '../../../hooks/useDirectoryQuery';
import UsersTableRow from './UsersTableRow';
const UsersTable = ({ workspace = 'local' }): ReactElement => {
......@@ -81,7 +81,7 @@ const UsersTable = ({ workspace = 'local' }): ReactElement => {
const query = useDirectoryQuery({ text: debouncedText, current, itemsPerPage }, [sortBy, sortDirection], 'users', workspace);
const getDirectoryData = useEndpoint('GET', '/v1/directory');
const { data, isFetched, isLoading } = useQuery(['getDirectoryData', query], () => getDirectoryData(query));
const { data, isFetched, isLoading, isError, refetch } = useQuery(['getDirectoryData', query], () => getDirectoryData(query));
const handleClick = useCallback(
(username) => (e: React.KeyboardEvent | React.MouseEvent) => {
......@@ -137,6 +137,15 @@ const UsersTable = ({ workspace = 'local' }): ReactElement => {
<StatesTitle>{t('No_results_found')}</StatesTitle>
</States>
)}
{isError && (
<States>
<StatesIcon name='warning' variation='danger' />
<StatesTitle>{t('Something_went_wrong')}</StatesTitle>
<StatesActions>
<StatesAction onClick={() => refetch()}>{t('Reload_page')}</StatesAction>
</StatesActions>
</States>
)}
</>
);
};
......
......@@ -2,9 +2,9 @@ import type { IUser, Serialized } from '@rocket.chat/core-typings';
import { Box, Flex, TableRow, TableCell } from '@rocket.chat/fuselage';
import React from 'react';
import MarkdownText from '../../../components/MarkdownText';
import UserAvatar from '../../../components/avatar/UserAvatar';
import { useFormatDate } from '../../../hooks/useFormatDate';
import MarkdownText from '../../../../../components/MarkdownText';
import UserAvatar from '../../../../../components/avatar/UserAvatar';
import { useFormatDate } from '../../../../../hooks/useFormatDate';
type UsersTableRowProps = {
user: Serialized<IUser> & { domain?: string };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment