Skip to content

Commit

Permalink
feat: Omnichannel directory chats filters (#33016)
Browse files Browse the repository at this point in the history
  • Loading branch information
dougfabris committed Oct 15, 2024
1 parent f294348 commit 61c8cec
Show file tree
Hide file tree
Showing 15 changed files with 645 additions and 125 deletions.
8 changes: 8 additions & 0 deletions .changeset/giant-singers-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@rocket.chat/core-typings': minor
'@rocket.chat/rest-typings': minor
'@rocket.chat/i18n': minor
'@rocket.chat/meteor': minor
---

Adds filters options to the omnichannel directory chats tab
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Callout, Pagination } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import type { GETLivechatRoomsParams } from '@rocket.chat/rest-typings';
import { usePermission } from '@rocket.chat/ui-contexts';
import { usePermission, useRouter } from '@rocket.chat/ui-contexts';
import { hashQueryKey } from '@tanstack/react-query';
import moment from 'moment';
import type { ComponentProps, ReactElement } from 'react';
Expand Down Expand Up @@ -131,6 +131,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
const [customFields, setCustomFields] = useState<{ [key: string]: string }>();

const { t } = useTranslation();
const directoryPath = useRouter().buildRoutePath('/omnichannel-directory');

const canRemoveClosedChats = usePermission('remove-closed-livechat-room');
const { enabled: isPriorityEnabled } = useOmnichannelPriorities();
Expand Down Expand Up @@ -204,7 +205,11 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
<GenericTableCell withTruncatedText data-qa='current-chats-cell-status'>
<RoomActivityIcon room={room} /> {getStatusText(open, onHold, !!servedBy?.username)}
</GenericTableCell>
{canRemoveClosedChats && !open && <RemoveChatButton _id={_id} />}
{canRemoveClosedChats && (
<GenericTableCell maxHeight='x36' fontScale='p2' color='hint' withTruncatedText data-qa='current-chats-cell-delete'>
{!open && <RemoveChatButton _id={_id} />}
</GenericTableCell>
)}
</GenericTableRow>
);
},
Expand Down Expand Up @@ -304,10 +309,7 @@ const CurrentChatsPage = ({ id, onRowClick }: { id?: string; onRowClick: (_id: s
<Callout type='warning' title={t('This_page_will_be_deprecated_soon')}>
<Trans i18nKey='Manage_conversations_in_the_contact_center'>
Manage conversations in the
<a target='_blank' href='https://go.rocket.chat/i/omnichannel-docs'>
contact center
</a>
.
<a href={directoryPath}>contact center</a>.
</Trans>
</Callout>
{((isSuccess && data?.rooms.length > 0) || queryHasChanged) && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { IconButton } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
import { useSetModal, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import GenericModal from '../../../components/GenericModal';
import { GenericTableCell } from '../../../components/GenericTable';
import { useRemoveCurrentChatMutation } from './hooks/useRemoveCurrentChatMutation';

type RemoveChatButtonProps = { _id: string };

const RemoveChatButton = ({ _id }: RemoveChatButtonProps) => {
const removeCurrentChatMutation = useRemoveCurrentChatMutation();
const t = useTranslation();
const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();
const t = useTranslation();

const handleRemoveClick = useMutableCallback(async () => {
const removeCurrentChatMutation = useRemoveCurrentChatMutation();

const handleRemoveClick = useEffectEvent(async () => {
removeCurrentChatMutation.mutate(_id);
});

const handleDelete = useMutableCallback((e) => {
const handleDelete = useEffectEvent((e) => {
e.stopPropagation();
const onDeleteAgent = async (): Promise<void> => {
const onDeleteAgent = async () => {
try {
await handleRemoveClick();
dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
Expand All @@ -31,27 +31,18 @@ const RemoveChatButton = ({ _id }: RemoveChatButtonProps) => {
setModal(null);
};

const handleClose = (): void => {
setModal(null);
};

setModal(
<GenericModal
variant='danger'
data-qa-id='current-chats-modal-remove'
onConfirm={onDeleteAgent}
onClose={handleClose}
onCancel={handleClose}
onCancel={() => setModal(null)}
confirmText={t('Delete')}
/>,
);
});

return (
<GenericTableCell maxHeight='x36' fontScale='p2' color='hint' withTruncatedText data-qa='current-chats-cell-delete'>
<IconButton small icon='trash' title={t('Remove')} disabled={removeCurrentChatMutation.isLoading} onClick={handleDelete} />
</GenericTableCell>
);
return <IconButton danger small icon='trash' title={t('Remove')} disabled={removeCurrentChatMutation.isLoading} onClick={handleDelete} />;
};

export default RemoveChatButton;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts';
import React from 'react';

import ContactHistoryMessagesList from '../contactHistory/MessageList/ContactHistoryMessagesList';
import ChatFiltersContextualBar from './chats/ChatFiltersContextualBar';

const ChatsContextualBar = () => {
const router = useRouter();
Expand All @@ -11,6 +12,10 @@ const ChatsContextualBar = () => {
const handleOpenRoom = () => id && router.navigate(`/live/${id}`);
const handleClose = () => router.navigate('/omnichannel-directory/chats');

if (context === 'filters') {
return <ChatFiltersContextualBar onClose={handleClose} />;
}

if (context === 'info' && id) {
return <ContactHistoryMessagesList chatId={id} onClose={handleClose} onOpenRoom={handleOpenRoom} />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import CallTab from './calls/CallTab';
import ChatTab from './chats/ChatTab';
import ContactTab from './contacts/ContactTab';

const DEFAULT_TAB = 'contacts';
const DEFAULT_TAB = 'chats';

const OmnichannelDirectoryPage = () => {
const t = useTranslation();
Expand Down Expand Up @@ -39,19 +39,19 @@ const OmnichannelDirectoryPage = () => {
<Page>
<PageHeader title={t('Omnichannel_Contact_Center')} />
<Tabs flexShrink={0}>
<Tabs.Item selected={tab === 'contacts'} onClick={() => handleTabClick('contacts')}>
{t('Contacts')}
</Tabs.Item>
<Tabs.Item selected={tab === 'chats'} onClick={() => handleTabClick('chats')}>
{t('Chats')}
</Tabs.Item>
<Tabs.Item selected={tab === 'contacts'} onClick={() => handleTabClick('contacts')}>
{t('Contacts')}
</Tabs.Item>
<Tabs.Item selected={tab === 'calls'} onClick={() => handleTabClick('calls')}>
{t('Calls')}
</Tabs.Item>
</Tabs>
<PageContent>
{tab === 'contacts' && <ContactTab />}
{tab === 'chats' && <ChatTab />}
{tab === 'contacts' && <ContactTab />}
{tab === 'calls' && <CallTab />}
</PageContent>
</Page>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Box, Button, Chip } from '@rocket.chat/fuselage';
import { useEffectEvent } from '@rocket.chat/fuselage-hooks';
import { GenericMenu } from '@rocket.chat/ui-client';
import { useMethod, useRoute, useSetModal, useToastMessageDispatch, useTranslation } from '@rocket.chat/ui-contexts';
import { useQueryClient } from '@tanstack/react-query';
import React from 'react';

import FilterByText from '../../../../components/FilterByText';
import GenericModal from '../../../../components/GenericModal';
import { useChatsFilters } from './useChatsFilters';

const ChatFilterByText = () => {
const t = useTranslation();
const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();
const directoryRoute = useRoute('omnichannel-directory');
const removeClosedChats = useMethod('livechat:removeAllClosedRooms');
const queryClient = useQueryClient();

const { displayFilters, setFiltersQuery, removeFilter } = useChatsFilters();

const handleRemoveAllClosed = useEffectEvent(async () => {
const onDeleteAll = async () => {
try {
await removeClosedChats();
queryClient.invalidateQueries(['current-chats']);
dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
} finally {
setModal(null);
}
};

setModal(
<GenericModal
variant='danger'
data-qa-id='current-chats-modal-remove-all-closed'
onConfirm={onDeleteAll}
onCancel={() => setModal(null)}
confirmText={t('Delete')}
/>,
);
});

const menuItems = [
{
items: [
{
id: 'delete-all-closed-chats',
variant: 'danger',
icon: 'trash',
content: t('Delete_all_closed_chats'),
onClick: handleRemoveAllClosed,
} as const,
],
},
];

return (
<>
<FilterByText onChange={(text) => setFiltersQuery((prevState) => ({ ...prevState, guest: text }))}>
<Button
onClick={() =>
directoryRoute.push({
tab: 'chats',
context: 'filters',
})
}
icon='customize'
>
{t('Filters')}
</Button>
<GenericMenu placement='bottom-end' detached title={t('More')} sections={menuItems} />
</FilterByText>
<Box display='flex' flexWrap='wrap' mbe={4}>
{Object.entries(displayFilters).map(([value, label], index) => {
if (!label) {
return null;
}

return (
<Chip mie={8} mbe={8} key={index} onClick={() => removeFilter(value)}>
{label}
</Chip>
);
})}
</Box>
</>
);
};

export default ChatFilterByText;
Loading

0 comments on commit 61c8cec

Please sign in to comment.