diff --git a/apps/meteor/client/hooks/roomActions/useRoomInfoRoomAction.ts b/apps/meteor/client/hooks/roomActions/useRoomInfoRoomAction.ts index 1b7608ff5212..573211b6fbd8 100644 --- a/apps/meteor/client/hooks/roomActions/useRoomInfoRoomAction.ts +++ b/apps/meteor/client/hooks/roomActions/useRoomInfoRoomAction.ts @@ -2,7 +2,7 @@ import { lazy, useMemo } from 'react'; import type { RoomToolboxActionConfig } from '../../views/room/contexts/RoomToolboxContext'; -const ChatsContextualBar = lazy(() => import('../../views/omnichannel/directory/chats/contextualBar/ChatsContextualBar')); +const ChatsContextualBar = lazy(() => import('../../views/omnichannel/directory/chats/ChatInfo/ChatInfoRouter')); export const useRoomInfoRoomAction = () => { return useMemo( diff --git a/apps/meteor/client/views/omnichannel/contactHistory/ContactHistory.tsx b/apps/meteor/client/views/omnichannel/contactHistory/ContactHistory.tsx index 955c1918be05..5375132c455f 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/ContactHistory.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/ContactHistory.tsx @@ -11,7 +11,7 @@ const ContactHistory = () => { return ( <> {chatId && chatId !== '' ? ( - + ) : ( )} diff --git a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx index 3bf6385a19a0..70e2619be58e 100644 --- a/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx +++ b/apps/meteor/client/views/omnichannel/contactHistory/MessageList/ContactHistoryMessagesList.tsx @@ -1,12 +1,24 @@ -import { Box, Icon, Margins, States, StatesIcon, StatesSubtitle, StatesTitle, TextInput, Throbber } from '@rocket.chat/fuselage'; +import { + Box, + Button, + ButtonGroup, + ContextualbarFooter, + Icon, + Margins, + States, + StatesIcon, + StatesSubtitle, + StatesTitle, + TextInput, + Throbber, +} from '@rocket.chat/fuselage'; import { useSetting, useTranslation, useUserPreference } from '@rocket.chat/ui-contexts'; -import type { ChangeEvent, Dispatch, ReactElement, SetStateAction } from 'react'; +import type { ChangeEvent, ReactElement } from 'react'; import React, { useMemo, useState } from 'react'; import { Virtuoso } from 'react-virtuoso'; import { ContextualbarHeader, - ContextualbarAction, ContextualbarIcon, ContextualbarTitle, ContextualbarClose, @@ -21,18 +33,17 @@ import { isMessageSequential } from '../../../room/MessageList/lib/isMessageSequ import ContactHistoryMessage from './ContactHistoryMessage'; import { useHistoryMessageList } from './useHistoryMessageList'; -const ContactHistoryMessagesList = ({ - chatId, - setChatId, - close, -}: { +type ContactHistoryMessagesListProps = { chatId: string; - setChatId: Dispatch>; - close: () => void; -}): ReactElement => { - const [text, setText] = useState(''); + onClose: () => void; + onOpenRoom?: () => void; +}; + +const ContactHistoryMessagesList = ({ chatId, onClose, onOpenRoom }: ContactHistoryMessagesListProps) => { const t = useTranslation(); + const [text, setText] = useState(''); const showUserAvatar = !!useUserPreference('displayAvatars'); + const { itemsList: messageList, loadMoreItems } = useHistoryMessageList( useMemo(() => ({ roomId: chatId, filter: text }), [chatId, text]), ); @@ -47,10 +58,9 @@ const ContactHistoryMessagesList = ({ return ( <> - setChatId('')} title={t('Back')} name='arrow-back' /> {t('Chat_History')} - + @@ -111,6 +121,13 @@ const ContactHistoryMessagesList = ({ )} + {onOpenRoom && ( + + + + + + )} ); }; diff --git a/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx b/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx index b7bad83f87e1..7493a72becb2 100644 --- a/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx +++ b/apps/meteor/client/views/omnichannel/directory/ChatsContextualBar.tsx @@ -1,75 +1,21 @@ -import { Box } from '@rocket.chat/fuselage'; -import { useRoute, useRouteParameter, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRouter } from '@rocket.chat/ui-contexts'; import React from 'react'; -import { - Contextualbar, - ContextualbarHeader, - ContextualbarIcon, - ContextualbarTitle, - ContextualbarAction, - ContextualbarClose, -} from '../../../components/Contextualbar'; -import Chat from './chats/Chat'; -import ChatInfoDirectory from './chats/contextualBar/ChatInfoDirectory'; -import { RoomEditWithData } from './chats/contextualBar/RoomEdit'; -import { FormSkeleton } from './components'; -import { useOmnichannelRoomInfo } from './hooks/useOmnichannelRoomInfo'; - -const ChatsContextualBar = ({ chatReload }: { chatReload?: () => void }) => { - const t = useTranslation(); - - const directoryRoute = useRoute('omnichannel-directory'); +import ContactHistoryMessagesList from '../contactHistory/MessageList/ContactHistoryMessagesList'; +const ChatsContextualBar = () => { + const router = useRouter(); const context = useRouteParameter('context'); - const id = useRouteParameter('id') || ''; - - const openInRoom = () => id && directoryRoute.push({ tab: 'chats', id, context: 'view' }); - - const handleClose = () => directoryRoute.push({ tab: 'chats' }); - - const handleCancel = () => id && directoryRoute.push({ tab: 'chats', id, context: 'info' }); + const id = useRouteParameter('id'); - const { data: room, isLoading, isError, refetch: reloadInfo } = useOmnichannelRoomInfo(id); - - if (context === 'view' && id) { - return ; - } - - if (isLoading) { - return ( - - - - ); - } + const handleOpenRoom = () => id && router.navigate(`/live/${id}`); + const handleClose = () => router.navigate('/omnichannel-directory/chats'); - if (isError || !room) { - return {t('Room_not_found')}; + if (context === 'info' && id) { + return ; } - return ( - - - {context === 'info' && ( - <> - - {t('Room_Info')} - - - )} - {context === 'edit' && ( - <> - - {t('edit-room')} - - )} - - - {context === 'info' && } - {context === 'edit' && id && } - - ); + return null; }; export default ChatsContextualBar; diff --git a/apps/meteor/client/views/omnichannel/directory/ContextualBarRouter.tsx b/apps/meteor/client/views/omnichannel/directory/ContextualBarRouter.tsx index 6ffd6383c9df..0ab0cdb59162 100644 --- a/apps/meteor/client/views/omnichannel/directory/ContextualBarRouter.tsx +++ b/apps/meteor/client/views/omnichannel/directory/ContextualBarRouter.tsx @@ -5,14 +5,14 @@ import CallsContextualBarDirectory from './CallsContextualBarDirectory'; import ChatsContextualBar from './ChatsContextualBar'; import ContactContextualBar from './ContactContextualBar'; -const ContextualBarRouter = ({ chatReload }: { chatReload?: () => void }) => { +const ContextualBarRouter = () => { const tab = useRouteParameter('tab'); switch (tab) { case 'contacts': return ; case 'chats': - return ; + return ; case 'calls': return ; default: diff --git a/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx b/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx index dff1cd467b02..34d52bbfb399 100644 --- a/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx +++ b/apps/meteor/client/views/omnichannel/directory/OmnichannelDirectoryPage.tsx @@ -4,7 +4,6 @@ import React, { useEffect, useCallback } from 'react'; import { ContextualbarDialog } from '../../../components/Contextualbar'; import { Page, PageHeader, PageContent } from '../../../components/Page'; -import { queryClient } from '../../../lib/queryClient'; import ContextualBarRouter from './ContextualBarRouter'; import CallTab from './calls/CallTab'; import ChatTab from './chats/ChatTab'; @@ -35,8 +34,6 @@ const OmnichannelDirectoryPage = () => { const handleTabClick = useCallback((tab) => router.navigate({ name: 'omnichannel-directory', params: { tab } }), [router]); - const chatReload = () => queryClient.invalidateQueries({ queryKey: ['current-chats'] }); - return ( @@ -46,7 +43,7 @@ const OmnichannelDirectoryPage = () => { {t('Contacts')} handleTabClick('chats')}> - {t('Chats' as any /* TODO: this is going to change to Conversations */)} + {t('Chats')} handleTabClick('calls')}> {t('Calls')} @@ -60,7 +57,7 @@ const OmnichannelDirectoryPage = () => { {context && ( - + )} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/ChatInfo.js similarity index 100% rename from apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js rename to apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/ChatInfo.js diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatsContextualBar.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/ChatInfoRouter.tsx similarity index 91% rename from apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatsContextualBar.tsx rename to apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/ChatInfoRouter.tsx index d78f7e9a4ab9..28d938de8271 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatsContextualBar.tsx +++ b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/ChatInfoRouter.tsx @@ -5,7 +5,7 @@ import { ContextualbarHeader, ContextualbarIcon, ContextualbarTitle, Contextualb import { useRoom } from '../../../../room/contexts/RoomContext'; import { useRoomToolbox } from '../../../../room/contexts/RoomToolboxContext'; import ChatInfo from './ChatInfo'; -import { RoomEditWithData } from './RoomEdit'; +import RoomEdit from './RoomEdit'; const PATH = 'live'; @@ -36,7 +36,7 @@ const ChatsContextualBar = () => { {context === 'edit' ? ( - + ) : ( )} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/DepartmentField.tsx similarity index 100% rename from apps/meteor/client/views/omnichannel/directory/chats/contextualBar/DepartmentField.tsx rename to apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/DepartmentField.tsx diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEdit.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/RoomEdit.tsx similarity index 100% rename from apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEdit.tsx rename to apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/RoomEdit.tsx diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEditWithData.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/RoomEditWithData.tsx similarity index 100% rename from apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/RoomEditWithData.tsx rename to apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/RoomEditWithData.tsx diff --git a/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/index.ts b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/index.ts new file mode 100644 index 000000000000..99205f80da8b --- /dev/null +++ b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/RoomEdit/index.ts @@ -0,0 +1 @@ +export { default } from './RoomEditWithData'; diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/VisitorClientInfo.js b/apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/VisitorClientInfo.js similarity index 100% rename from apps/meteor/client/views/omnichannel/directory/chats/contextualBar/VisitorClientInfo.js rename to apps/meteor/client/views/omnichannel/directory/chats/ChatInfo/VisitorClientInfo.js diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js deleted file mode 100644 index ed7b5fdafb86..000000000000 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js +++ /dev/null @@ -1,178 +0,0 @@ -import { Box, Margins, Tag, Button, ButtonGroup } from '@rocket.chat/fuselage'; -import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useToastMessageDispatch, useRoute, useUserSubscription, useTranslation } from '@rocket.chat/ui-contexts'; -import { Meteor } from 'meteor/meteor'; -import moment from 'moment'; -import React, { useEffect, useMemo, useState } from 'react'; - -import { hasPermission } from '../../../../../../app/authorization/client'; -import { ContextualbarScrollableContent, ContextualbarFooter } from '../../../../../components/Contextualbar'; -import { InfoPanelField, InfoPanelLabel, InfoPanelText } from '../../../../../components/InfoPanel'; -import MarkdownText from '../../../../../components/MarkdownText'; -import { useEndpointData } from '../../../../../hooks/useEndpointData'; -import { useFormatDateAndTime } from '../../../../../hooks/useFormatDateAndTime'; -import { useFormatDuration } from '../../../../../hooks/useFormatDuration'; -import CustomField from '../../../components/CustomField'; -import { AgentField, ContactField, SlaField } from '../../components'; -import PriorityField from '../../components/PriorityField'; -import { formatQueuedAt } from '../../utils/formatQueuedAt'; -import DepartmentField from './DepartmentField'; -import VisitorClientInfo from './VisitorClientInfo'; - -function ChatInfoDirectory({ id, route = undefined, room }) { - const t = useTranslation(); - - const formatDateAndTime = useFormatDateAndTime(); - const { value: allCustomFields, phase: stateCustomFields } = useEndpointData('/v1/livechat/custom-fields'); - const [customFields, setCustomFields] = useState([]); - const formatDuration = useFormatDuration(); - - const { - ts, - tags, - closedAt, - departmentId, - v, - servedBy, - metrics, - topic, - waitingResponse, - responseBy, - slaId, - priorityId, - livechatData, - queuedAt, - } = room || { room: { v: {} } }; - - const routePath = useRoute(route || 'omnichannel-directory'); - const canViewCustomFields = () => hasPermission('view-livechat-room-customfields'); - const subscription = useUserSubscription(id); - const hasGlobalEditRoomPermission = hasPermission('save-others-livechat-room-info'); - const hasLocalEditRoomPermission = servedBy?._id === Meteor.userId(); - const visitorId = v?._id; - const queueStartedAt = queuedAt || ts; - - const queueTime = useMemo(() => formatQueuedAt(room), [room]); - - const dispatchToastMessage = useToastMessageDispatch(); - useEffect(() => { - if (allCustomFields) { - const { customFields: customFieldsAPI } = allCustomFields; - setCustomFields(customFieldsAPI); - } - }, [allCustomFields, stateCustomFields]); - - const checkIsVisibleAndScopeRoom = (key) => { - const field = customFields.find(({ _id }) => _id === key); - if (field && field.visibility === 'visible' && field.scope === 'room') { - return true; - } - return false; - }; - - const onEditClick = useMutableCallback(() => { - const hasEditAccess = !!subscription || hasLocalEditRoomPermission || hasGlobalEditRoomPermission; - if (!hasEditAccess) { - return dispatchToastMessage({ type: 'error', message: t('Not_authorized') }); - } - - routePath.push({ - tab: route ? 'room-info' : 'chats', - context: 'edit', - id, - }); - }); - - return ( - <> - - - {room && v && } - {visitorId && } - {servedBy && } - {departmentId && } - {tags && tags.length > 0 && ( - - {t('Tags')} - - {tags.map((tag) => ( - - - {tag} - - - ))} - - - )} - {topic && ( - - {t('Topic')} - - - - - )} - {queueStartedAt && ( - - {t('Queue_Time')} - {queueTime} - - )} - {closedAt && ( - - {t('Chat_Duration')} - {moment(closedAt).from(moment(ts), true)} - - )} - {ts && ( - - {t('Created_at')} - {formatDateAndTime(ts)} - - )} - {closedAt && ( - - {t('Closed_At')} - {formatDateAndTime(closedAt)} - - )} - {servedBy?.ts && ( - - {t('Taken_at')} - {formatDateAndTime(servedBy.ts)} - - )} - {metrics?.response?.avg && formatDuration(metrics.response.avg) && ( - - {t('Avg_response_time')} - {formatDuration(metrics.response.avg)} - - )} - {!waitingResponse && responseBy?.lastMessageTs && ( - - {t('Inactivity_Time')} - {moment(responseBy.lastMessageTs).fromNow(true)} - - )} - {canViewCustomFields() && - livechatData && - Object.keys(livechatData).map( - (key) => checkIsVisibleAndScopeRoom(key) && livechatData[key] && , - )} - {slaId && } - {priorityId && } - - - - - - - - - ); -} - -export default ChatInfoDirectory; diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/index.ts b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/index.ts deleted file mode 100644 index dcfd1c14d2d9..000000000000 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/RoomEdit/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as RoomEditWithData } from './RoomEditWithData'; diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts index 323d41550592..b1f64b17b94c 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-send-pdf-transcript.spec.ts @@ -79,7 +79,7 @@ test.describe('omnichannel- export chat transcript as PDF', () => { await agent.poHomeChannel.transcript.contactCenterSearch.type(newUser.name); await page.waitForTimeout(3000); await agent.poHomeChannel.transcript.firstRow.click(); - await agent.poHomeChannel.transcript.viewFullConversation.click(); + await agent.poHomeChannel.transcript.btnOpenChat.click(); await agent.poHomeChannel.content.btnSendTranscript.click(); await expect(agent.poHomeChannel.content.btnSendTranscriptAsPDF).toHaveAttribute('aria-disabled', 'false'); await agent.poHomeChannel.content.btnSendTranscriptAsPDF.click(); diff --git a/apps/meteor/tests/e2e/page-objects/omnichannel-transcript.ts b/apps/meteor/tests/e2e/page-objects/omnichannel-transcript.ts index d0249933d438..d7ece60a5ef6 100644 --- a/apps/meteor/tests/e2e/page-objects/omnichannel-transcript.ts +++ b/apps/meteor/tests/e2e/page-objects/omnichannel-transcript.ts @@ -36,8 +36,8 @@ export class OmnichannelTranscript { return this.page.locator('//tr[1]//td[1]'); } - get viewFullConversation(): Locator { - return this.page.locator('//button[@title="View full conversation"]/i'); + get btnOpenChat(): Locator { + return this.page.getByRole('dialog').getByRole('button', { name: 'Open chat', exact: true }); } get DownloadedPDF(): Locator { diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 91b1dab69ee9..142691224076 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1006,6 +1006,7 @@ "Chatops_Enabled": "Enable Chatops", "Chatops_Title": "Chatops Panel", "Chatops_Username": "Chatops Username", + "Chats": "Chats", "Chat_Duration": "Chat Duration", "Chats_removed": "Chats Removed", "Check_All": "Check All", @@ -4040,6 +4041,7 @@ "Open_call": "Open call", "Open_call_in_new_tab": "Open call in new tab", "Open_channel_user_search": "`%s` - Open Channel / User search", + "Open_chat": "Open chat", "Open_conversations": "Open Conversations", "Open_Days": "Open days", "Open_days_of_the_week": "Open Days of the Week",