Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
8d73776
new quick action buttons at top chat
rafaelblink Mar 3, 2021
b2e163d
change color and order of close button
rafaelblink Mar 4, 2021
ecd0e0c
header to accept color attr
rafaelblink Mar 4, 2021
1b132f2
return chat queue modal
rafaelblink Mar 4, 2021
76031c2
transcript modal core
rafaelblink Mar 4, 2021
30f2d99
removing unnecessary comments and create forward chat modal.
rafaelblink Mar 4, 2021
682c58d
return queue modal with method
rafaelblink Mar 5, 2021
3d143ee
start transcript modal
rafaelblink Mar 5, 2021
9e66b7d
discard transcript initialized
rafaelblink Mar 8, 2021
dc614f6
already transcript request raw message at the center
rafaelblink Mar 9, 2021
293ac25
Forward Chat modal component in place
rafaelblink Mar 9, 2021
0f3f90f
temporary unused value
rafaelblink Mar 9, 2021
5c26331
return queue modal with footer buttons end aligned
rafaelblink Mar 10, 2021
6a0448e
User and department auto completes in place and start with Forward ev…
rafaelblink Mar 10, 2021
c541a74
transfering chats via quick buttons
rafaelblink Mar 11, 2021
c1fb33b
transfering chats via quick buttons
rafaelblink Mar 11, 2021
7b1ab91
Merge remote-tracking branch 'origin/omnichannel/new-quick-action-but…
rafaelblink Mar 11, 2021
3984971
merge
rafaelblink Mar 11, 2021
9e79cfb
show quick actions only on livechat rooms and remove unused props.
rafaelblink Mar 12, 2021
b18a44b
show transcript email requested
rafaelblink Mar 12, 2021
7f3c5a2
start permissions at actions buttons
rafaelblink Mar 12, 2021
defee01
missed import
rafaelblink Mar 12, 2021
26299e1
close permission
rafaelblink Mar 12, 2021
1327b48
return to queue permissions
rafaelblink Mar 13, 2021
b60dd2e
polishing permissions and implements send transcript button
rafaelblink Mar 15, 2021
7584bc6
Merge branch 'develop' into omnichannel/new-quick-action-buttons
rafaelblink Mar 15, 2021
0b6e430
improve labels and textarea size.
rafaelblink Mar 15, 2021
ffe35ee
missed interface status attr
rafaelblink Mar 15, 2021
de52caa
id optional attr
rafaelblink Mar 15, 2021
2de4a63
close modal in place
rafaelblink Mar 16, 2021
17bd1b2
Merge branch 'develop' into omnichannel/new-quick-action-buttons
renatobecker Mar 19, 2021
d39c37e
transform tsx into js in order to pass on tests.
rafaelblink Mar 19, 2021
6daa1d9
Update packages/rocketchat-i18n/i18n/en.i18n.json
rafaelblink Mar 19, 2021
a1d35e5
change useEndpointAction to useEndpoint
rafaelblink Mar 19, 2021
cc75315
Merge remote-tracking branch 'origin/omnichannel/new-quick-action-but…
rafaelblink Mar 19, 2021
dbad9a6
change modals omnichannel directory
rafaelblink Mar 19, 2021
397a129
solve github LGTM
rafaelblink Mar 19, 2021
1494c23
Merge branch 'develop' into omnichannel/new-quick-action-buttons
renatobecker Mar 19, 2021
e25cc96
remove unnecessary stuff
rafaelblink Mar 19, 2021
4e9240b
Merge remote-tracking branch 'origin/omnichannel/new-quick-action-but…
rafaelblink Mar 19, 2021
c4f8886
Merge branch 'develop' into omnichannel/new-quick-action-buttons
rafaelblink Mar 23, 2021
c7777cb
change useEndPoint to useEndpointData
rafaelblink Mar 23, 2021
671cb7f
change transcript already requested to the top
rafaelblink Mar 23, 2021
5689b61
useAutoFocus and use mutable callbacks
rafaelblink Mar 23, 2021
0330a03
Gazzo PR review changes
rafaelblink Mar 23, 2021
c5ba86e
using useMethod instead useMethod
rafaelblink Mar 23, 2021
44f3c1b
fix tests
rafaelblink Mar 23, 2021
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
69 changes: 69 additions & 0 deletions client/components/CloseChatModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { FC, useCallback, useEffect, useRef, useState, useMemo } from 'react';
import { Field, Button, TextInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage';

import { useTranslation } from '../contexts/TranslationContext';
import { useForm } from '../hooks/useForm';
import { useComponentDidUpdate } from '../hooks/useComponentDidUpdate';


type CloseChatModalProps = {
onCancel: () => void;
onConfirm: (comment: string) => void;
};

const CloseChatModal: FC<CloseChatModalProps> = ({ onCancel, onConfirm, ...props }) => {
const t = useTranslation();

const ref = useRef<HTMLInputElement>();

useEffect(() => {
if (typeof ref?.current?.focus === 'function') {
ref.current.focus();
}
}, [ref]);

const { values, handlers } = useForm({ comment: '' });

const { comment } = values as { comment: string };
const { handleComment } = handlers;
const [commentError, setCommentError] = useState('');


const handleConfirm = useCallback(() => {
onConfirm(comment);
}, [comment, onConfirm]);

useComponentDidUpdate(() => {
setCommentError(!comment ? t('The_field_is_required', t('Comment')) : '');
}, [t, comment]);

const canConfirm = useMemo(() => !!comment, [comment]);

return <Modal {...props}>
<Modal.Header>
<Icon name='baloon-close-top-right' size={20}/>
<Modal.Title>{t('Closing_chat')}</Modal.Title>
<Modal.Close onClick={onCancel}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
<Box color='neutral-600'>{t('Close_room_description')}</Box>
<Field marginBlock='x15'>
<Field.Label>{t('Comment')}*</Field.Label>
<Field.Row>
<TextInput error={commentError} flexGrow={1} value={comment} onChange={handleComment} placeholder={t('Please_add_a_comment')} />
</Field.Row>
<Field.Error>
{commentError}
</Field.Error>
</Field>
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
<Button onClick={onCancel}>{t('Cancel')}</Button>
<Button disabled={!canConfirm} primary onClick={handleConfirm}>{t('Confirm')}</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>;
};

export default CloseChatModal;
98 changes: 98 additions & 0 deletions client/components/ForwardChatModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Field, Button, TextAreaInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';

import { useTranslation } from '../contexts/TranslationContext';
import { useForm } from '../hooks/useForm';
import ModalSeparator from './ModalSeparator';
import DepartmentAutoComplete from '../views/omnichannel/DepartmentAutoComplete';
import { UserAutoComplete } from './AutoComplete';
import { useEndpointAction } from '../hooks/useEndpointAction';

type ForwardChatModalProps = {
onForward: (departmentName?: string, userId?: string, comment?: string) => void;
onCancel: () => void;
};

const ForwardChatModal: FC<ForwardChatModalProps> = ({ onForward, onCancel, ...props }) => {
const t = useTranslation();

const ref = useRef<HTMLInputElement>();

useEffect(() => {
if (typeof ref?.current?.focus === 'function') {
ref.current.focus();
}
}, [ref]);

const { values, handlers } = useForm({ departmentName: '', username: '', comment: '' });
const { departmentName, username, comment } = values as { departmentName: string; username: string; comment: string };
const [userId, setUserId] = useState('');

const { handleDepartmentName, handleUsername, handleComment } = handlers;
const userInfo = useEndpointAction('GET', `users.info?username=${ username }`);


const handleSend = useCallback(() => {
onForward(departmentName, userId, comment);
}, [onForward, departmentName, userId, comment]);


const onChangeDepartment = useMutableCallback((departmentId: string): void => {
handleDepartmentName(departmentId);
handleUsername('');
setUserId('');
});

const onChangeUsername = useMutableCallback((username: string): void => {
handleUsername(username);
handleDepartmentName('');
});

useEffect(() => {
if (!username) { return; }
const fetchData = async (): Promise<void> => {
const { user } = await userInfo();
setUserId(user._id);
};
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [username]);

return <Modal {...props}>
<Modal.Header>
<Icon name='baloon-arrow-top-right' size={20}/>
<Modal.Title>{t('Forward_chat')}</Modal.Title>
<Modal.Close onClick={onCancel}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
<Field mbe={'x30'}>
<Field.Label>{t('Forward_to_department')}</Field.Label>
<Field.Row>
<DepartmentAutoComplete value={departmentName} onChange={onChangeDepartment} flexShrink={1} placeholder={t('Department_name')} />
</Field.Row>
</Field>
<ModalSeparator text={t('or')} />
<Field mbs={'x30'}>
<Field.Label>{t('Forward_to_user')}</Field.Label>
<Field.Row>
<UserAutoComplete flexGrow={1} value={username} onChange={onChangeUsername} placeholder={t('Username')} />
</Field.Row>
</Field>
<Field marginBlock='x15'>
<Field.Label>{t('Leave_a_comment')} <Box is='span' color='neutral-600'>({t('Optional')})</Box></Field.Label>
<Field.Row>
<TextAreaInput rows={8} flexGrow={1} value={comment} onChange={handleComment} />
</Field.Row>
</Field>
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
<Button onClick={onCancel}>{t('Cancel')}</Button>
<Button primary onClick={handleSend}>{t('Forward')}</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>;
};

export default ForwardChatModal;
3 changes: 2 additions & 1 deletion client/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const HeaderIcon: FC<{ icon: JSX.Element | { name: string; color?: string } | nu

const ToolBox: FC = (props: any) => <ButtonGroup mi='x4' medium {...props}/>;

const ToolBoxAction: FC = ({ id, icon, title, action, className, tabId, index, ...props }: any) => <ActionButton
const ToolBoxAction: FC = ({ id, icon, color, title, action, className, tabId, index, ...props }: any) => <ActionButton
className={className}
primary={tabId === id}
onClick={action}
Expand All @@ -23,6 +23,7 @@ const ToolBoxAction: FC = ({ id, icon, title, action, className, tabId, index, .
ghost
tiny
overflow='visible'
color={color && color}
{...props}
/>;

Expand Down
7 changes: 7 additions & 0 deletions client/components/ModalSeparator/ModalSeparator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React, { FC } from 'react';
Comment thread
rafaelblink marked this conversation as resolved.

import './style.css';

const ModalSeparator: FC<{ text: string }> = ({ text, ...props }) => <h6 className='modal-separator' {...props}>{text}</h6>;

export default ModalSeparator;
3 changes: 3 additions & 0 deletions client/components/ModalSeparator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ModalSeparator from './ModalSeparator';

export default ModalSeparator;
20 changes: 20 additions & 0 deletions client/components/ModalSeparator/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.modal-separator {
display: flex;

text-align: center;
text-transform: uppercase;

font-size: 0.625rem;
font-weight: normal;
}

.modal-separator::before,
.modal-separator::after {
flex: 1;

margin: auto 0.8em;

content: '';

border-bottom: solid 2px #f2f3f5;
}
44 changes: 44 additions & 0 deletions client/components/ReturnChatQueueModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Box, Button, ButtonGroup, Icon, Modal } from '@rocket.chat/fuselage';
import React, { FC } from 'react';

import { useTranslation } from '../contexts/TranslationContext';
import { withDoNotAskAgain, RequiredModalProps } from './withDoNotAskAgain';

type ReturnChatQueueModalProps = RequiredModalProps & {
onMoveChat: () => void;
onCancel: () => void;
};

const ReturnChatQueueModal: FC<ReturnChatQueueModalProps> = ({
onCancel,
onMoveChat,
confirm = onMoveChat,
dontAskAgain,
...props
}) => {
const t = useTranslation();

return <Modal {...props}>
<Modal.Header>
<Icon name='burger-arrow-left' size={20}/>
Comment thread
rafaelblink marked this conversation as resolved.
<Modal.Title>{t('Return_to_the_queue')}</Modal.Title>
<Modal.Close onClick={onCancel}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
{t('Would_you_like_to_return_the_queue')}
</Modal.Content>
<Modal.Footer>
<Box>
{dontAskAgain}
<ButtonGroup align='end'>
<Button onClick={onCancel}>{t('Cancel')}</Button>
<Button primary onClick={confirm}>{t('Move_queue')}</Button>
</ButtonGroup>
</Box>
</Modal.Footer>
</Modal>;
};

export const ReturnChatQueueDoNotAskAgain = withDoNotAskAgain<ReturnChatQueueModalProps>(ReturnChatQueueModal);

export default ReturnChatQueueModal;
111 changes: 111 additions & 0 deletions client/components/TranscriptModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { FC, useCallback, useEffect, useRef, useState, useMemo } from 'react';
import { Field, Button, TextInput, Icon, ButtonGroup, Modal } from '@rocket.chat/fuselage';

import { useTranslation } from '../contexts/TranslationContext';
import { useForm } from '../hooks/useForm';
import { useComponentDidUpdate } from '../hooks/useComponentDidUpdate';
import { IRoom } from '../../definition/IRoom';


type TranscriptModalProps = {
email: string;
room?: IRoom;
onRequest: (email: string, subject: string) => void;
onSend?: (email: string, subject: string, token: string) => void;
onCancel: () => void;
onDiscard: () => void;
};

const TranscriptModal: FC<TranscriptModalProps> = ({ email: emailDefault = '', room, onRequest, onSend, onCancel, onDiscard, ...props }) => {
const t = useTranslation();

const ref = useRef<HTMLInputElement>();

useEffect(() => {
if (typeof ref?.current?.focus === 'function') {
ref.current.focus();
}
}, [ref]);

const { values, handlers } = useForm({ email: emailDefault || '', subject: t('Transcript_of_your_livechat_conversation') });

const { email, subject } = values as { email: string; subject: string };
const { handleEmail, handleSubject } = handlers;
const [emailError, setEmailError] = useState('');
const [subjectError, setSubjectError] = useState('');
const { transcriptRequest } = room as unknown as IRoom;
const roomOpen = room && room.open;
const token = room?.v?.token;


const handleRequest = useCallback(() => {
onRequest(email, subject);
}, [email, onRequest, subject]);

const handleSend = useCallback(() => {
onSend && token && onSend(email, subject, token);
}, [email, onSend, subject, token]);

const handleDiscard = useCallback(() => onDiscard(), [onDiscard]);

useComponentDidUpdate(() => {
setEmailError(!email ? t('The_field_is_required', t('Email')) : '');
}, [t, email]);

useComponentDidUpdate(() => {
setSubjectError(!subject ? t('The_field_is_required', t('Subject')) : '');
}, [t, subject]);

const canSave = useMemo(() => !!subject, [subject]);

useEffect(() => {
if (transcriptRequest) {
handleEmail(transcriptRequest.email);
handleSubject(transcriptRequest.subject);
}
});

return <Modal {...props}>
<Modal.Header>
<Icon name='mail-arrow-top-right' size={20}/>
<Modal.Title>{t('Transcript')}</Modal.Title>
<Modal.Close onClick={onCancel}/>
</Modal.Header>
<Modal.Content fontScale='p1'>
<Field marginBlock='x15'>
<Field.Label>{t('Email')}*</Field.Label>
<Field.Row>
<TextInput disabled={!!emailDefault || !!transcriptRequest} error={emailError} flexGrow={1} value={email} onChange={handleEmail} />
</Field.Row>
<Field.Error>
{emailError}
</Field.Error>
</Field>
<Field marginBlock='x15'>
<Field.Label>{t('Subject')}*</Field.Label>
<Field.Row>
<TextInput ref={ref} disabled={!!transcriptRequest} error={subjectError} flexGrow={1} value={subject} onChange={handleSubject} />
</Field.Row>
<Field.Error>
{subjectError}
</Field.Error>
</Field>
{!!transcriptRequest && <p>{t('Livechat_transcript_already_requested_warning')}</p>}
</Modal.Content>
<Modal.Footer>
<ButtonGroup align='end'>
<Button onClick={onCancel}>{t('Cancel')}</Button>
{
roomOpen && transcriptRequest
? <Button primary danger onClick={handleDiscard}>{t('Discard')}</Button>
: <Button disabled={!canSave} primary onClick={handleRequest}>{t('Request')}</Button>
}
{
!roomOpen && <Button disabled={!canSave} primary onClick={handleSend}>{t('Send')}</Button>
}
</ButtonGroup>
</Modal.Footer>
</Modal>;
};

export default TranscriptModal;
3 changes: 2 additions & 1 deletion client/providers/OmniChannelProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ const OmnichannelEnabledProvider: FC = ({ children }) => {
setContextValue((context) => ({
...context,
agentAvailable: user?.statusLivechat === 'available',
routeConfig,
Comment thread
rafaelblink marked this conversation as resolved.
Outdated
}));
}, [user?.statusLivechat]);
}, [user?.statusLivechat, routeConfig]);

if (!routeConfig || !user) {
return <OmnichannelDisabledProvider children={children}/>;
Expand Down
Loading