Chore: Refactor Directory Tables (#27646)
parent
973a1d900f
commit
decefab53c
@ -1,170 +0,0 @@ |
||||
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; |
||||
@ -1,136 +0,0 @@ |
||||
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; |
||||
@ -0,0 +1,147 @@ |
||||
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; |
||||
@ -0,0 +1,61 @@ |
||||
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; |
||||
@ -0,0 +1 @@ |
||||
export { default } from './ChannelsTable'; |
||||
@ -0,0 +1,129 @@ |
||||
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; |
||||
@ -0,0 +1,51 @@ |
||||
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; |
||||
@ -0,0 +1 @@ |
||||
export { default } from './TeamsTable'; |
||||
Loading…
Reference in new issue