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

chore: Improve Omnichannel Contact Center routing #33943

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { lazy, useMemo } from 'react';

import type { RoomToolboxActionConfig } from '../../views/room/contexts/RoomToolboxContext';

const ContactsContextualBar = lazy(() => import('../../views/omnichannel/directory/contacts/contextualBar/ContactsContextualBar'));
const ContactInfoRouter = lazy(() => import('../../views/omnichannel/directory/contacts/contactInfo/ContactInfoRouter'));

export const useContactProfileRoomAction = () => {
return useMemo(
Expand All @@ -11,7 +11,7 @@ export const useContactProfileRoomAction = () => {
groups: ['live' /* , 'voip'*/],
title: 'Contact_Info',
icon: 'user',
tabComponent: ContactsContextualBar,
tabComponent: ContactInfoRouter,
order: 1,
}),
[],
Expand Down
12 changes: 5 additions & 7 deletions apps/meteor/client/startup/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const IndexRoute = lazy(() => import('../views/root/IndexRoute'));
const MeetRoute = lazy(() => import('../views/meet/MeetRoute'));
const HomePage = lazy(() => import('../views/home/HomePage'));
const DirectoryPage = lazy(() => import('../views/directory'));
const OmnichannelDirectoryPage = lazy(() => import('../views/omnichannel/directory/OmnichannelDirectoryPage'));
const OmnichannelDirectoryRouter = lazy(() => import('../views/omnichannel/directory/OmnichannelDirectoryRouter'));
const OmnichannelQueueList = lazy(() => import('../views/omnichannel/queueList'));
const CMSPage = lazy(() => import('@rocket.chat/web-ui-registration').then(({ CMSPage }) => ({ default: CMSPage })));
const SecretURLPage = lazy(() => import('../views/invite/SecretURLPage'));
Expand Down Expand Up @@ -48,10 +48,8 @@ declare module '@rocket.chat/ui-contexts' {
pattern: '/directory/:tab?';
};
'omnichannel-directory': {
pathname: `/omnichannel-directory${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}${
| `/${string}`
| ''}`;
pattern: '/omnichannel-directory/:page?/:bar?/:id?/:tab?/:context?';
pathname: `/omnichannel-directory${`/${string}` | ''}${`/${string}` | ''}${`/${string}` | ''}`;
pattern: '/omnichannel-directory/:tab?/:context?/:id?/';
};
'livechat-queue': {
pathname: '/livechat-queue';
Expand Down Expand Up @@ -153,11 +151,11 @@ router.defineRoutes([
),
},
{
path: '/omnichannel-directory/:page?/:bar?/:id?/:tab?/:context?',
path: '/omnichannel-directory/:tab?/:context?/:id?/',
id: 'omnichannel-directory',
element: appLayout.wrap(
<MainLayout>
<OmnichannelDirectoryPage />
<OmnichannelDirectoryRouter />
</MainLayout>,
),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ import { VoipInfo } from './calls/contextualBar/VoipInfo';
import { FormSkeleton } from './components/FormSkeleton';

const CallsContextualBarDirectory = () => {
const directoryRoute = useRoute('omnichannel-directory');
const { t } = useTranslation();

const bar = useRouteParameter('bar') || 'info';
const id = useRouteParameter('id');
const token = useSearchParameter('token');
const context = useRouteParameter('context');

const { t } = useTranslation();
const directoryRoute = useRoute('omnichannel-directory');

const handleClose = (): void => {
directoryRoute.push({ page: 'calls' });
directoryRoute.push({ tab: 'calls' });
};

const query = useMemo(
Expand All @@ -34,7 +34,7 @@ const CallsContextualBarDirectory = () => {

const { value: data, phase: state, error } = useEndpointData(`/v1/voip/room`, { params: query });

if (bar === 'view' && id) {
if (context === 'view' && id) {
return <Call rid={id} />;
}

Expand All @@ -52,7 +52,7 @@ const CallsContextualBarDirectory = () => {

const room = data.room as unknown as IVoipRoom; // TODO Check why types are incompatible even though the endpoint returns an IVoipRooms

return <Contextualbar>{bar === 'info' && <VoipInfo room={room} onClickClose={handleClose} />}</Contextualbar>;
return <Contextualbar>{context === 'info' && <VoipInfo room={room} onClickClose={handleClose} />}</Contextualbar>;
};

export default CallsContextualBarDirectory;
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,23 @@ import { RoomEditWithData } from './chats/contextualBar/RoomEdit';
import { FormSkeleton } from './components';
import { useOmnichannelRoomInfo } from './hooks/useOmnichannelRoomInfo';

type ChatsContextualBarProps = { chatReload?: () => void };
const ChatsContextualBar = ({ chatReload }: { chatReload?: () => void }) => {
const { t } = useTranslation();

const ChatsContextualBar = ({ chatReload }: ChatsContextualBarProps) => {
const directoryRoute = useRoute('omnichannel-directory');

const bar = useRouteParameter('bar') || 'info';
const context = useRouteParameter('context');
const id = useRouteParameter('id') || '';

const { t } = useTranslation();

const openInRoom = (): void => {
id && directoryRoute.push({ page: 'chats', id, bar: 'view' });
};
const openInRoom = () => id && directoryRoute.push({ tab: 'chats', id, context: 'view' });

const handleChatsContextualbarCloseButtonClick = (): void => {
directoryRoute.push({ page: 'chats' });
};
const handleClose = () => directoryRoute.push({ tab: 'chats' });

const handleChatsContextualbarBackButtonClick = (): void => {
id && directoryRoute.push({ page: 'chats', id, bar: 'info' });
};
const handleCancel = () => id && directoryRoute.push({ tab: 'chats', id, context: 'info' });

const { data: room, isLoading, isError, refetch: reloadInfo } = useOmnichannelRoomInfo(id);

if (bar === 'view' && id) {
if (context === 'view' && id) {
return <Chat rid={id} />;
}

Expand All @@ -60,25 +52,23 @@ const ChatsContextualBar = ({ chatReload }: ChatsContextualBarProps) => {
return (
<Contextualbar>
<ContextualbarHeader expanded>
{bar === 'info' && (
{context === 'info' && (
<>
<ContextualbarIcon name='info-circled' />
<ContextualbarTitle>{t('Room_Info')}</ContextualbarTitle>
<ContextualbarAction title={t('View_full_conversation')} name='new-window' onClick={openInRoom} />
</>
)}
{bar === 'edit' && (
{context === 'edit' && (
<>
<ContextualbarIcon name='pencil' />
<ContextualbarTitle>{t('edit-room')}</ContextualbarTitle>
</>
)}
<ContextualbarClose onClick={handleChatsContextualbarCloseButtonClick} />
<ContextualbarClose onClick={handleClose} />
</ContextualbarHeader>
{bar === 'info' && <ChatInfoDirectory id={id} room={room} />}
{bar === 'edit' && (
<RoomEditWithData id={id} reload={chatReload} reloadInfo={reloadInfo} onClose={handleChatsContextualbarBackButtonClick} />
)}
{context === 'info' && <ChatInfoDirectory id={id} room={room} />}
{context === 'edit' && id && <RoomEditWithData id={id} reload={chatReload} reloadInfo={reloadInfo} onClose={handleCancel} />}
</Contextualbar>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
ContextualbarTitle,
ContextualbarClose,
} from '../../../components/Contextualbar';
import ContactEditWithData from './contacts/contextualBar/ContactEditWithData';
import ContactInfo from './contacts/contextualBar/ContactInfo';
import ContactNewEdit from './contacts/contextualBar/ContactNewEdit';
import ContactInfo from './contacts/contactInfo/ContactInfo';
import EditContactInfo from './contacts/contactInfo/EditContactInfo';
import EditContactInfoWithData from './contacts/contactInfo/EditContactInfoWithData';

const HEADER_OPTIONS = {
new: { icon: 'user', title: 'New_contact' },
Expand All @@ -22,18 +22,19 @@ const HEADER_OPTIONS = {
type BarOptions = keyof typeof HEADER_OPTIONS;

const ContactContextualBar = () => {
const { t } = useTranslation();

const directoryRoute = useRoute('omnichannel-directory');
const bar = (useRouteParameter('bar') || 'info') as BarOptions;
const contactId = useRouteParameter('id') || '';
const context = useRouteParameter('context');

const { t } = useTranslation();

const handleContactsContextualbarCloseButtonClick = () => {
directoryRoute.push({ page: 'contacts' });
const handleClose = () => {
directoryRoute.push({ tab: 'contacts' });
};

const handleContactsContextualbarBackButtonClick = () => {
directoryRoute.push({ page: 'contacts', id: contactId, bar: 'info' });
const handleCancel = () => {
directoryRoute.push({ tab: 'contacts', context: 'info', id: contactId });
};

const header = useMemo(() => HEADER_OPTIONS[bar] || HEADER_OPTIONS.info, [bar]);
Expand All @@ -43,11 +44,11 @@ const ContactContextualBar = () => {
<ContextualbarHeader>
<ContextualbarIcon name={header.icon} />
<ContextualbarTitle>{t(header.title)}</ContextualbarTitle>
<ContextualbarClose onClick={handleContactsContextualbarCloseButtonClick} />
<ContextualbarClose onClick={handleClose} />
</ContextualbarHeader>
{bar === 'new' && <ContactNewEdit id={contactId} close={handleContactsContextualbarCloseButtonClick} />}
{bar === 'info' && <ContactInfo id={contactId} />}
{bar === 'edit' && <ContactEditWithData id={contactId} close={handleContactsContextualbarBackButtonClick} />}
{context === 'new' && <EditContactInfo id={contactId} onCancel={handleClose} />}
{context === 'edit' && <EditContactInfoWithData id={contactId} onCancel={handleCancel} />}
{context !== 'new' && context !== 'edit' && <ContactInfo id={contactId} />}
</Contextualbar>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@ import CallsContextualBarDirectory from './CallsContextualBarDirectory';
import ChatsContextualBar from './ChatsContextualBar';
import ContactContextualBar from './ContactContextualBar';

type ContextualBarProps = {
chatReload?: () => void;
};

const ContextualBar = ({ chatReload }: ContextualBarProps) => {
const page = useRouteParameter('page');
const ContextualBarRouter = ({ chatReload }: { chatReload?: () => void }) => {
const tab = useRouteParameter('tab');

switch (page) {
switch (tab) {
case 'contacts':
return <ContactContextualBar />;
case 'chats':
Expand All @@ -24,4 +20,4 @@ const ContextualBar = ({ chatReload }: ContextualBarProps) => {
}
};

export default ContextualBar;
export default ContextualBarRouter;
Original file line number Diff line number Diff line change
@@ -1,71 +1,66 @@
import { Tabs } from '@rocket.chat/fuselage';
import { useRouteParameter, usePermission, useTranslation, useRouter } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import { useRouteParameter, useTranslation, useRouter } from '@rocket.chat/ui-contexts';
import React, { useEffect, useCallback } from 'react';

import { ContextualbarDialog } from '../../../components/Contextualbar';
import { Page, PageHeader, PageContent } from '../../../components/Page';
import { queryClient } from '../../../lib/queryClient';
import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage';
import ContextualBar from './ContextualBar';
import ContextualBarRouter from './ContextualBarRouter';
import CallTab from './calls/CallTab';
import ChatTab from './chats/ChatTab';
import ContactTab from './contacts/ContactTab';

const DEFAULT_TAB = 'contacts';

const OmnichannelDirectoryPage = (): ReactElement => {
const OmnichannelDirectoryPage = () => {
const t = useTranslation();
const router = useRouter();
const page = useRouteParameter('page');
const bar = useRouteParameter('bar');
const canViewDirectory = usePermission('view-omnichannel-contact-center');
const tab = useRouteParameter('tab');
const context = useRouteParameter('context');

useEffect(
() =>
router.subscribeToRouteChange(() => {
if (router.getRouteName() !== 'omnichannel-directory' || !!router.getRouteParameters().page) {
if (router.getRouteName() !== 'omnichannel-directory' || !!router.getRouteParameters().tab) {
return;
}

router.navigate({
name: 'omnichannel-directory',
params: { page: DEFAULT_TAB },
params: { tab: DEFAULT_TAB },
});
}),
[router],
);

const handleTabClick = useCallback((tab) => () => router.navigate({ name: 'omnichannel-directory', params: { tab } }), [router]);
const handleTabClick = useCallback((tab) => router.navigate({ name: 'omnichannel-directory', params: { tab } }), [router]);

const chatReload = () => queryClient.invalidateQueries({ queryKey: ['current-chats'] });

if (!canViewDirectory) {
return <NotAuthorizedPage />;
}

return (
<Page flexDirection='row'>
<Page>
<PageHeader title={t('Omnichannel_Contact_Center')} />
<Tabs flexShrink={0}>
<Tabs.Item selected={page === 'contacts'} onClick={handleTabClick('contacts')}>
<Tabs.Item selected={tab === 'contacts'} onClick={() => handleTabClick('contacts')}>
{t('Contacts')}
</Tabs.Item>
<Tabs.Item selected={page === 'chats'} onClick={handleTabClick('chats')}>
{t('Chats' as 'color')}
<Tabs.Item selected={tab === 'chats'} onClick={() => handleTabClick('chats')}>
{t('Chats' as any /* TODO: this is going to change to Conversations */)}
</Tabs.Item>
<Tabs.Item selected={page === 'calls'} onClick={handleTabClick('calls')}>
{t('Calls' as 'color')}
<Tabs.Item selected={tab === 'calls'} onClick={() => handleTabClick('calls')}>
{t('Calls')}
</Tabs.Item>
</Tabs>
<PageContent>
{(page === 'contacts' && <ContactTab />) || (page === 'chats' && <ChatTab />) || (page === 'calls' && <CallTab />)}
{tab === 'contacts' && <ContactTab />}
{tab === 'chats' && <ChatTab />}
{tab === 'calls' && <CallTab />}
</PageContent>
</Page>
{bar && (
{context && (
<ContextualbarDialog>
<ContextualBar chatReload={chatReload} />
<ContextualBarRouter chatReload={chatReload} />
</ContextualbarDialog>
)}
</Page>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { usePermission } from '@rocket.chat/ui-contexts';
import React from 'react';

import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage';
import OmnichannelDirectoryPage from './OmnichannelDirectoryPage';

const OmnichannelDirectoryRouter = () => {
const canViewDirectory = usePermission('view-omnichannel-contact-center');

if (!canViewDirectory) {
return <NotAuthorizedPage />;
}

return <OmnichannelDirectoryPage />;
};

export default OmnichannelDirectoryRouter;
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ const CallTable = () => {
const onRowClick = useMutableCallback((id, token) => {
directoryRoute.push(
{
page: 'calls',
bar: 'info',
tab: 'calls',
context: 'info',
id,
},
{ token },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ const ChatTable = () => {

const onRowClick = useMutableCallback((id) =>
directoryRoute.push({
page: 'chats',
bar: 'info',
tab: 'chats',
context: 'info',
id,
}),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,11 @@ function ChatInfoDirectory({ id, route = undefined, room }: ChatInfoDirectoryPro
return dispatchToastMessage({ type: 'error', message: t('Not_authorized') });
}

routePath.push(
route
? {
tab: 'room-info',
context: 'edit',
id,
}
: {
page: 'chats',
id,
bar: 'edit',
},
);
routePath.push({
tab: route ? 'room-info' : 'chats',
context: 'edit',
id,
});
});

return (
Expand Down
Loading
Loading