Skip to content
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
3 changes: 2 additions & 1 deletion app/livechat/imports/server/rest/departments.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ API.v1.addRoute('livechat/department', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();
const { text } = this.queryParams;
const { text, enabled } = this.queryParams;

const departments = Promise.await(findDepartments({
userId: this.userId,
text,
enabled,
pagination: {
offset,
count,
Expand Down
3 changes: 2 additions & 1 deletion app/livechat/server/api/lib/departments.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { escapeRegExp } from '../../../../../lib/escapeRegExp';
import { hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission';
import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models/server/raw';

export async function findDepartments({ userId, text, pagination: { offset, count, sort } }) {
export async function findDepartments({ userId, text, enabled, pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(userId, 'view-livechat-departments') && !await hasPermissionAsync(userId, 'view-l-room')) {
throw new Error('error-not-authorized');
}

const query = {
...enabled && { enabled: Boolean(enabled) },
...text && { name: new RegExp(escapeRegExp(text), 'i') },
};

Expand Down
6 changes: 4 additions & 2 deletions client/components/AutoComplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { AutoComplete, Option, Options } from '@rocket.chat/fuselage';
import UserAvatar from './avatar/UserAvatar';
import { useEndpointData } from '../hooks/useEndpointData';

const query = (term = '') => ({ selector: JSON.stringify({ term }) });
const query = (term = '', conditions = {}) => ({ selector: JSON.stringify({ term, conditions }) });

const Avatar = ({ value, ...props }) => <UserAvatar size={Options.AvatarSize} username={value} {...props} />;

export const UserAutoComplete = React.memo((props) => {
const { conditions = {} } = props;
const [filter, setFilter] = useState('');
const { value: data } = useEndpointData('users.autocomplete', useMemo(() => query(filter), [filter]));
// eslint-disable-next-line react-hooks/exhaustive-deps
const { value: data } = useEndpointData('users.autocomplete', useMemo(() => query(filter, conditions), [filter]));
const options = useMemo(() => (data && data.items.map((user) => ({ value: user.username, label: user.name }))) || [], [data]);
return <AutoComplete
{...props}
Expand Down
45 changes: 30 additions & 15 deletions client/components/Omnichannel/modals/ForwardChatModal.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Field, Button, TextAreaInput, Icon, ButtonGroup, Modal, Box } from '@rocket.chat/fuselage';
import { useMutableCallback, useAutoFocus } from '@rocket.chat/fuselage-hooks';

Expand All @@ -7,9 +7,10 @@ import { useForm } from '../../../hooks/useForm';
import ModalSeparator from '../../ModalSeparator';
import DepartmentAutoComplete from '../../../views/omnichannel/DepartmentAutoComplete';
import { UserAutoComplete } from '../../AutoComplete';
import { useEndpoint } from '../../../contexts/ServerContext';
import { useEndpointData } from '../../../hooks/useEndpointData';

const ForwardChatModal = ({ onForward, onCancel, ...props }) => {
const ForwardChatModal = ({ onForward, onCancel, room, ...props }) => {
const t = useTranslation();

const inputRef = useAutoFocus(true);
Expand All @@ -19,8 +20,8 @@ const ForwardChatModal = ({ onForward, onCancel, ...props }) => {
const [userId, setUserId] = useState('');

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

const getUserData = useEndpoint('GET', `users.info?username=${ username }`);
const { value: departmentsData = {} } = useEndpointData('livechat/department', useMemo(() => ({ enabled: true }), []));

const handleSend = useMutableCallback(() => {
onForward(departmentName, userId, comment);
Expand All @@ -41,31 +42,45 @@ const ForwardChatModal = ({ onForward, onCancel, ...props }) => {
useEffect(() => {
if (!username) { return; }
const fetchData = async () => {
const { user } = value;
const { user } = await getUserData();
setUserId(user._id);
};
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [username]);

const canForward = departmentName || username;

const { departments } = departmentsData;

const hasDepartments = departments && departments.length > 0;

const { servedBy: { _id: agentId } = {} } = room || {};

const _id = agentId && { $ne: agentId };

const conditions = { _id, status: { $ne: 'offline' }, statusLivechat: 'available' };

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'}>
{ hasDepartments && <>
<Field mbe={'x30'}>
<Field.Label>{t('Forward_to_department')}</Field.Label>
<Field.Row>
<DepartmentAutoComplete enabled={true} value={departmentName} onChange={onChangeDepartment} flexShrink={1} placeholder={t('Department_name')} />
</Field.Row>
</Field>
<ModalSeparator text={t('or')} />
</> }
<Field mbs={hasDepartments && 'x30'}>
<Field.Label>{t('Forward_to_user')}</Field.Label>
<Field.Row>
<UserAutoComplete flexGrow={1} value={username} onChange={onChangeUsername} placeholder={t('Username')} />
<UserAutoComplete conditions={conditions} flexGrow={1} value={username} onChange={onChangeUsername} placeholder={t('Username')} />
</Field.Row>
</Field>
<Field marginBlock='x15'>
Expand All @@ -78,7 +93,7 @@ const ForwardChatModal = ({ onForward, onCancel, ...props }) => {
<Modal.Footer>
<ButtonGroup align='end'>
<Button onClick={onCancel}>{t('Cancel')}</Button>
<Button primary onClick={handleSend}>{t('Forward')}</Button>
<Button disabled={!canForward} primary onClick={handleSend}>{t('Forward')}</Button>
</ButtonGroup>
</Modal.Footer>
</Modal>;
Expand Down
5 changes: 3 additions & 2 deletions client/views/omnichannel/DepartmentAutoComplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks';

import { useEndpointData } from '../../hooks/useEndpointData';

const query = (term = '') => ({ selector: JSON.stringify({ term }) });
const query = (term = '', enabled = false) => ({ selector: JSON.stringify({ term, ...enabled && { conditions: { enabled } } }) });

const DepartmentAutoComplete = React.memo((props) => {
const { enabled } = props;
const [filter, setFilter] = useState('');
const { value: data } = useEndpointData('livechat/department.autocomplete', useMemo(() => query(filter), [filter]));
const { value: data } = useEndpointData('livechat/department.autocomplete', useMemo(() => query(filter, enabled), [enabled, filter]));
const options = useMemo(() => (data && data.items.map((department) => ({ value: department._id, label: department.name }))) || [], [data]);
const onClickRemove = useMutableCallback(() => props.onChange(''));
return <AutoComplete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const QuickActions = ({ room, className }: { room: IRoom; className: BoxProps['c
setModal(<TranscriptModal room={room} email={email} onRequest={handleRequestTranscript} onSend={handleSendTranscript} onDiscard={handleDiscardTranscript} onCancel={closeModal} />);
break;
case QuickActionsEnum.ChatForward:
setModal(<ForwardChatModal onForward={handleForwardChat} onCancel={closeModal} />);
setModal(<ForwardChatModal room={room} onForward={handleForwardChat} onCancel={closeModal} />);
break;
case QuickActionsEnum.CloseChat:
setModal(<CloseChatModal onConfirm={handleClose} onCancel={closeModal} />);
Expand Down