Skip to content

Commit

Permalink
Merge branch 'feat/single-contact-id' into feat/contact-blocking
Browse files Browse the repository at this point in the history
  • Loading branch information
tapiarafael authored Oct 18, 2024
2 parents d39957c + b923f2d commit 666dd02
Show file tree
Hide file tree
Showing 12 changed files with 435 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
MessageSystemName,
MessageSystemBody,
MessageSystemTimestamp,
Bubble,
} from '@rocket.chat/fuselage';
import { UserAvatar } from '@rocket.chat/ui-avatar';
import React, { memo } from 'react';
Expand Down Expand Up @@ -79,7 +80,13 @@ const ContactHistoryMessage = ({ message, sequential, isNewDay, showUserAvatar }

return (
<>
{isNewDay && <MessageDivider>{format(message.ts)}</MessageDivider>}
{isNewDay && (
<MessageDivider>
<Bubble small secondary>
{format(message.ts)}
</Bubble>
</MessageDivider>
)}
<MessageTemplate isPending={message.temp} sequential={sequential} role='listitem' data-qa='chat-history-message'>
<MessageLeftContainer>
{!sequential && message.u.username && showUserAvatar && (
Expand All @@ -95,7 +102,6 @@ const ContactHistoryMessage = ({ message, sequential, isNewDay, showUserAvatar }
)}
{sequential && <StatusIndicators message={message} />}
</MessageLeftContainer>

<MessageContainer>
{!sequential && (
<MessageHeaderTemplate>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import React from 'react';
import { ContextualbarHeader, ContextualbarIcon, ContextualbarTitle, ContextualbarClose } from '../../../../components/Contextualbar';
import { useFormatDate } from '../../../../hooks/useFormatDate';
import { useContactRoute } from '../../hooks/useContactRoute';
import ContactInfoChannels from '../tabs/ContactInfoChannels';
import ContactInfoChannels from '../tabs/ContactInfoChannels/ContactInfoChannels';
import ContactInfoDetails from '../tabs/ContactInfoDetails';
import ContactInfoHistory from '../tabs/ContactInfoHistory';

Expand Down Expand Up @@ -98,8 +98,8 @@ const ContactInfo = ({ contact, onClose }: ContactInfoProps) => {
customFieldEntries={customFieldEntries}
/>
)}
{context === 'channels' && <ContactInfoChannels />}
{context === 'history' && showContactHistory && <ContactInfoHistory />}
{context === 'channels' && contact?.channels && <ContactInfoChannels channels={contact?.channels} />}
{context === 'history' && showContactHistory && <ContactInfoHistory contactId={contact._id} />}
</>
);
};
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { ILivechatContactChannel, Serialized } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import { ContextualbarEmptyContent, ContextualbarScrollableContent } from '../../../../../components/Contextualbar';
import ContactInfoChannelsItem from './ContactInfoChannelsItem';

type ContactInfoChannelsProps = {
channels: Serialized<ILivechatContactChannel>[];
};

const ContactInfoChannels = ({ channels }: ContactInfoChannelsProps) => {
const t = useTranslation();

if (channels.length === 0) {
return <ContextualbarEmptyContent icon='balloon' title={t('No_channels_yet')} subtitle={t('No_channels_yet_description')} />;
}

return (
<>
<Box pbs={24} pis={24} mbe={8} fontScale='p2m'>
{t('Last_contacts')}
</Box>
<ContextualbarScrollableContent p={0}>
{channels.map((channel) => (
<ContactInfoChannelsItem key={channel.visitorId} {...channel} />
))}
</ContextualbarScrollableContent>
</>
);
};

export default ContactInfoChannels;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { ILivechatContactChannel, Serialized } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Box, Palette, IconButton } from '@rocket.chat/fuselage';
import { useTranslation, type TranslationKey } from '@rocket.chat/ui-contexts';
import React, { useState } from 'react';

import { OmnichannelRoomIcon } from '../../../../../components/RoomIcon/OmnichannelRoomIcon';
import { useTimeAgo } from '../../../../../hooks/useTimeAgo';

type ContactInfoChannelsItemProps = Serialized<ILivechatContactChannel>;

const sourceTypeMap: { [key: string]: string } = {
widget: 'Livechat',
email: 'Email',
sms: 'SMS',
app: 'Apps',
api: 'API',
other: 'Other',
};

const ContactInfoChannelsItem = ({ details, blocked, lastChat }: ContactInfoChannelsItemProps) => {
const t = useTranslation();
const timeAgo = useTimeAgo();
const [showButton, setShowButton] = useState(false);

const customClass = css`
&:hover,
&:focus {
background: ${Palette.surface['surface-hover']};
}
`;

return (
<Box
tabIndex={0}
borderBlockEndWidth={1}
borderBlockEndColor='stroke-extra-light'
borderBlockEndStyle='solid'
className={['rcx-box--animated', customClass]}
pi={24}
pb={8}
display='flex'
flexDirection='column'
onFocus={() => setShowButton(true)}
onPointerEnter={() => setShowButton(true)}
onPointerLeave={() => setShowButton(false)}
>
<Box display='flex' alignItems='center'>
{details && <OmnichannelRoomIcon source={details} size='x18' placement='default' />}
{details && (
<Box mi={4} fontScale='p2b'>
{t(sourceTypeMap[details?.type] as TranslationKey)} {blocked && `(${t('Blocked')})`}
</Box>
)}
{lastChat && (
<Box mis={4} fontScale='c1'>
{timeAgo(lastChat.ts)}
</Box>
)}
</Box>
<Box minHeight='x24' alignItems='center' mbs={4} display='flex' justifyContent='space-between'>
<Box>{details?.destination}</Box>
{showButton && <IconButton icon='menu' tiny />}
</Box>
</Box>
);
};

export default ContactInfoChannelsItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ContactInfoChannels';
Original file line number Diff line number Diff line change
@@ -1,16 +1,90 @@
import { ContextualbarEmptyContent } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import { Box, Margins, Throbber, States, StatesIcon, StatesTitle, Select } from '@rocket.chat/fuselage';
import { useLocalStorage } from '@rocket.chat/fuselage-hooks';
import { useEndpoint, useTranslation } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import { Virtuoso } from 'react-virtuoso';

const ContactInfoHistory = () => {
import { ContextualbarContent, ContextualbarEmptyContent } from '../../../../../components/Contextualbar';
import { VirtuosoScrollbars } from '../../../../../components/CustomScrollbars';
import ContactInfoHistoryItem from './ContactInfoHistoryItem';

type ContactInfoHistoryProps = {
contactId: string;
setChatId: (chatId: string) => void;
};

const ContactInfoHistory = ({ contactId, setChatId }: ContactInfoHistoryProps) => {
const t = useTranslation();
const isEmpty = true;

if (!isEmpty) {
return null;
}
const historyFilterOptions: [string, string][] = [
['all', t('All')],
['widget', t('Livechat')],
['email', t('Email')],
['sms', t('SMS')],
['app', t('Apps')],
['api', t('API')],
['other', t('Other')],
];

const [type, setType] = useLocalStorage<string>('contact-history-type', 'all');

const getContactHistory = useEndpoint('GET', '/v1/omnichannel/contacts.history');
const { data, isLoading, isError } = useQuery(['getContactHistory', contactId, type], () =>
getContactHistory({ contactId, source: type === 'all' ? undefined : type }),
);

return <ContextualbarEmptyContent icon='history' title={t('No_history_yet')} subtitle={t('No_history_yet_description')} />;
return (
<ContextualbarContent paddingInline={0}>
<Box
display='flex'
flexDirection='row'
p={24}
borderBlockEndWidth='default'
borderBlockEndStyle='solid'
borderBlockEndColor='extra-light'
flexShrink={0}
>
<Box display='flex' flexDirection='row' flexGrow={1} mi='neg-x4'>
<Margins inline={4}>
<Select value={type} onChange={(value) => setType(value as string)} placeholder={t('Filter')} options={historyFilterOptions} />
</Margins>
</Box>
</Box>
{isLoading && (
<Box pi={24} pb={12}>
<Throbber size='x12' />
</Box>
)}
{isError && (
<States>
<StatesIcon name='warning' variation='danger' />
<StatesTitle>{t('Something_went_wrong')}</StatesTitle>
</States>
)}
{data?.history.length === 0 && (
<ContextualbarEmptyContent icon='history' title={t('No_history_yet')} subtitle={t('No_history_yet_description')} />
)}
{!isError && data?.history && data.history.length > 0 && (
<>
<Box pi={24} pb={12}>
<Box is='span' color='hint' fontScale='p2'>
{t('Showing_current_of_total', { current: data?.history.length, total: data?.total })}
</Box>
</Box>
<Box flexGrow={1} flexShrink={1} overflow='hidden' display='flex'>
<Virtuoso
totalCount={data.history.length}
overscan={25}
data={data?.history}
components={{ Scroller: VirtuosoScrollbars }}
itemContent={(index, data) => <ContactInfoHistoryItem key={index} onClick={() => setChatId(data._id)} {...data} />}
/>
</Box>
</>
)}
</ContextualbarContent>
);
};

export default ContactInfoHistory;
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { Serialized } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Box, Palette, IconButton } from '@rocket.chat/fuselage';
import type { ContactSearchChatsResult } from '@rocket.chat/rest-typings';
import { useTranslation, type TranslationKey } from '@rocket.chat/ui-contexts';
import React from 'react';

import { OmnichannelRoomIcon } from '../../../../../components/RoomIcon/OmnichannelRoomIcon';
import { usePreventPropagation } from '../../../../../hooks/usePreventPropagation';
import { useTimeAgo } from '../../../../../hooks/useTimeAgo';

type ContactInfoHistoryItemProps = Serialized<ContactSearchChatsResult> & {
onClick: () => void;
};

const sourceTypeMap: { [key: string]: string } = {
widget: 'Livechat',
email: 'Email',
sms: 'SMS',
app: 'Apps',
api: 'API',
other: 'Other',
};

const ContactInfoHistoryItem = ({ source, lastMessage, closedAt, onClick }: ContactInfoHistoryItemProps) => {
const t = useTranslation();
const timeAgo = useTimeAgo();
const preventPropagation = usePreventPropagation();

const customClass = css`
&:hover {
cursor: pointer;
}
&:hover,
&:focus {
background: ${Palette.surface['surface-hover']};
}
`;

return (
<Box
tabIndex={0}
borderBlockEndWidth={1}
borderBlockEndColor='stroke-extra-light'
borderBlockEndStyle='solid'
className={['rcx-box--animated', customClass]}
pi={24}
pb={8}
display='flex'
flexDirection='column'
onClick={onClick}
>
<Box display='flex' alignItems='center'>
{source && <OmnichannelRoomIcon source={source} size='x18' placement='default' />}
{source && (
<Box mi={4} fontScale='p2b'>
{t(sourceTypeMap[source?.type] as TranslationKey)}
</Box>
)}
{lastMessage && (
<Box mis={4} fontScale='c1'>
{timeAgo(lastMessage.ts)}
</Box>
)}
</Box>
<Box minHeight='x24' alignItems='center' mbs={4} display='flex' justifyContent='space-between'>
<Box>{!closedAt ? t('Conversation_in_progress') : t('Conversation_closed_without_comment')}</Box>
<Box is='span' onClick={preventPropagation}>
<IconButton icon='warning' tiny />
</Box>
</Box>
</Box>
);
};

export default ContactInfoHistoryItem;
Loading

0 comments on commit 666dd02

Please sign in to comment.