Regression: Omnichannel Current Chat issues (#18718)

Co-authored-by: Guilherme Gazzo <guilherme@gazzo.xyz>
pull/18051/head^2
Martin Schoeler 5 years ago committed by GitHub
parent bc7a1b1251
commit a5118fa00f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      client/components/basic/AutoCompleteAgent.js
  2. 22
      client/components/basic/AutoCompleteDepartment.js
  3. 8
      client/omnichannel/agents/AgentsRoute.js
  4. 105
      client/omnichannel/currentChats/CurrentChatsPage.js
  5. 82
      client/omnichannel/currentChats/CurrentChatsRoute.js
  6. 8
      client/omnichannel/customFields/CustomFieldsTable.js
  7. 9
      client/omnichannel/managers/ManagersRoute.js
  8. 13
      client/omnichannel/triggers/TriggersTable.js
  9. 8
      ee/client/omnichannel/BusinessHoursTable.js
  10. 8
      ee/client/omnichannel/priorities/PrioritiesRoute.js
  11. 8
      ee/client/omnichannel/tags/TagsRoute.js
  12. 8
      ee/client/omnichannel/units/UnitsRoute.js
  13. 2
      packages/rocketchat-i18n/i18n/en.i18n.json

@ -0,0 +1,22 @@
import React, { useMemo, useState } from 'react';
import { AutoComplete, Option } from '@rocket.chat/fuselage';
import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
import { useTranslation } from '../../contexts/TranslationContext';
export const AutoCompleteAgent = React.memo((props) => {
const t = useTranslation();
const [filter, setFilter] = useState('');
const { data } = useEndpointDataExperimental('livechat/users/agent', useMemo(() => ({ text: filter }), [filter]));
const options = useMemo(() => (data && [{ value: 'all', label: t('All') }, ...data.users.map((user) => ({ value: user._id, label: user.name }))]) || [{ value: 'all', label: t('All') }], [data, t]);
return <AutoComplete
{...props}
filter={filter}
setFilter={setFilter}
renderSelected={({ label }) => <>{label}</>}
renderItem={({ value, ...props }) => <Option key={value} {...props} />}
options={ options }
/>;
});

@ -0,0 +1,22 @@
import React, { useMemo, useState } from 'react';
import { AutoComplete, Option } from '@rocket.chat/fuselage';
import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
import { useTranslation } from '../../contexts/TranslationContext';
export const AutoCompleteDepartment = React.memo((props) => {
const t = useTranslation();
const [filter, setFilter] = useState('');
const { data } = useEndpointDataExperimental('livechat/department', useMemo(() => ({ text: filter }), [filter]));
const options = useMemo(() => (data && [{ value: 'all', label: t('All') }, ...data.departments.map((department) => ({ value: department._id, label: department.name }))]) || [{ value: 'all', label: t('All') }], [data, t]);
return <AutoComplete
{...props}
filter={filter}
setFilter={setFilter}
renderSelected={({ label }) => <>{label}</>}
renderItem={({ value, ...props }) => <Option key={value} {...props} />}
options={ options }
/>;
});

@ -1,7 +1,7 @@
import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState } from 'react'; import React, { useMemo, useCallback, useState } from 'react';
import { Box, Table, Icon } from '@rocket.chat/fuselage'; import { Box, Table, Icon, Button } from '@rocket.chat/fuselage';
import { Th } from '../../components/GenericTable'; import { Th } from '../../components/GenericTable';
import { useTranslation } from '../../contexts/TranslationContext'; import { useTranslation } from '../../contexts/TranslationContext';
@ -49,7 +49,11 @@ export function RemoveAgentButton({ _id, reload }) {
setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>); setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
export function AgentInfoActions({ reload }) { export function AgentInfoActions({ reload }) {

@ -1,6 +1,6 @@
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { TextInput, Box, Icon, MultiSelect, Select, InputBox, Menu } from '@rocket.chat/fuselage'; import { TextInput, Box, Icon, MultiSelect, Select, InputBox, Menu } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useMutableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks';
import moment from 'moment'; import moment from 'moment';
import { useSubscription } from 'use-subscription'; import { useSubscription } from 'use-subscription';
@ -10,12 +10,13 @@ import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental'; import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
import { usePermission } from '../../contexts/AuthorizationContext'; import { usePermission } from '../../contexts/AuthorizationContext';
import { GenericTable } from '../../components/GenericTable'; import { GenericTable } from '../../components/GenericTable';
import { useForm } from '../../hooks/useForm';
import { useMethod } from '../../contexts/ServerContext'; import { useMethod } from '../../contexts/ServerContext';
import DeleteWarningModal from '../DeleteWarningModal';
import { useSetModal } from '../../contexts/ModalContext';
import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
import { AutoCompleteDepartment } from '../../components/basic/AutoCompleteDepartment';
import { AutoCompleteAgent } from '../../components/basic/AutoCompleteAgent';
// moment(new Date(from)).utc().format('YYYY-MM-DDTHH:mm:ss')
// guest: '', servedBy: '', status: '', department: '', from: '', to: ''
const Label = (props) => <Box fontScale='p2' color='default' {...props} />; const Label = (props) => <Box fontScale='p2' color='default' {...props} />;
const RemoveAllClosed = ({ handleClearFilters, handleRemoveClosed, ...props }) => { const RemoveAllClosed = ({ handleClearFilters, handleRemoveClosed, ...props }) => {
@ -43,38 +44,42 @@ const RemoveAllClosed = ({ handleClearFilters, handleRemoveClosed, ...props }) =
const FilterByText = ({ setFilter, reload, ...props }) => { const FilterByText = ({ setFilter, reload, ...props }) => {
const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();
const t = useTranslation(); const t = useTranslation();
const { data: departments } = useEndpointDataExperimental('livechat/department') || {}; const { data: allCustomFields } = useEndpointDataExperimental('livechat/custom-fields');
const { data: agents } = useEndpointDataExperimental('livechat/users/agent');
const depOptions = useMemo(() => (departments && departments.departments ? departments.departments.map(({ _id, name }) => [_id, name || _id]) : []), [departments]);
const agentOptions = useMemo(() => (agents && agents.users ? agents.users.map(({ _id, username }) => [_id, username || _id]) : []), [agents]);
const statusOptions = [['all', t('All')], ['closed', t('Closed')], ['opened', t('Open')]]; const statusOptions = [['all', t('All')], ['closed', t('Closed')], ['opened', t('Open')]];
const customFieldsOptions = useMemo(() => (allCustomFields && allCustomFields.customFields ? allCustomFields.customFields.map(({ _id, label }) => [_id, label]) : []), [allCustomFields]);
useEffect(() => {
!depOptions.find((dep) => dep[0] === 'all') && depOptions.unshift(['all', t('All')]); const [guest, setGuest] = useLocalStorage('guest', '');
}, [depOptions, t]); const [servedBy, setServedBy] = useLocalStorage('servedBy', 'all');
const [status, setStatus] = useLocalStorage('status', 'all');
const { values, handlers, reset } = useForm({ guest: '', servedBy: [], status: 'all', department: 'all', from: '', to: '', tags: [] }); const [department, setDepartment] = useLocalStorage('department', 'all');
const { const [from, setFrom] = useLocalStorage('from', '');
handleGuest, const [to, setTo] = useLocalStorage('to', '');
handleServedBy, const [tags, setTags] = useLocalStorage('tags', []);
handleStatus, const [customFields, setCustomFields] = useLocalStorage('tags', []);
handleDepartment,
handleFrom, const handleGuest = useMutableCallback((e) => setGuest(e.target.value));
handleTo, const handleServedBy = useMutableCallback((e) => setServedBy(e));
handleTags, const handleStatus = useMutableCallback((e) => setStatus(e));
} = handlers; const handleDepartment = useMutableCallback((e) => setDepartment(e));
const { const handleFrom = useMutableCallback((e) => setFrom(e.target.value));
guest, const handleTo = useMutableCallback((e) => setTo(e.target.value));
servedBy, const handleTags = useMutableCallback((e) => setTags(e));
status, const handleCustomFields = useMutableCallback((e) => setCustomFields(e));
department,
from, const reset = useMutableCallback(() => {
to, setGuest('');
tags, setServedBy('all');
} = values; setStatus('all');
setDepartment('all');
setFrom('');
setTo('');
setTags([]);
setCustomFields([]);
});
const forms = useSubscription(formsSubscription); const forms = useSubscription(formsSubscription);
@ -84,8 +89,11 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
const Tags = useCurrentChatTags(); const Tags = useCurrentChatTags();
const onSubmit = useMutableCallback((e) => e.preventDefault()); const onSubmit = useMutableCallback((e) => e.preventDefault());
const reducer = function(acc, curr) {
acc[curr] = '';
return acc;
};
useEffect(() => { useEffect(() => {
setFilter({ setFilter({
@ -96,8 +104,9 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
from: from && moment(new Date(from)).utc().format('YYYY-MM-DDTHH:mm:ss'), from: from && moment(new Date(from)).utc().format('YYYY-MM-DDTHH:mm:ss'),
to: to && moment(new Date(to)).utc().format('YYYY-MM-DDTHH:mm:ss'), to: to && moment(new Date(to)).utc().format('YYYY-MM-DDTHH:mm:ss'),
tags, tags,
customFields: customFields.reduce(reducer, {}),
}); });
}, [setFilter, guest, servedBy, status, department, from, to, tags]); }, [setFilter, guest, servedBy, status, department, from, to, tags, customFields]);
const handleClearFilters = useMutableCallback(() => { const handleClearFilters = useMutableCallback(() => {
reset(); reset();
@ -106,10 +115,21 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
const removeClosedChats = useMethod('livechat:removeAllClosedRooms'); const removeClosedChats = useMethod('livechat:removeAllClosedRooms');
const handleRemoveClosed = useMutableCallback(async () => { const handleRemoveClosed = useMutableCallback(async () => {
const onDeleteAll = async () => {
try {
await removeClosedChats(); await removeClosedChats();
reload(); reload();
dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
setModal();
};
setModal(<DeleteWarningModal onDelete={onDeleteAll} onCancel={() => setModal()}/>);
}); });
return <Box mb='x16' is='form' onSubmit={onSubmit} display='flex' flexDirection='column' {...props}> return <Box mb='x16' is='form' onSubmit={onSubmit} display='flex' flexDirection='column' {...props}>
<Box display='flex' flexDirection='row' flexWrap='wrap' {...props}> <Box display='flex' flexDirection='row' flexWrap='wrap' {...props}>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'> <Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
@ -118,11 +138,11 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
</Box> </Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'> <Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Served_By')}:</Label> <Label mb='x4'>{t('Served_By')}:</Label>
<MultiSelect flexShrink={0} options={agentOptions} value={servedBy} onChange={handleServedBy} placeholder={t('Served_By')}/> <AutoCompleteAgent value={servedBy} onChange={handleServedBy}/>
</Box> </Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'> <Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Department')}:</Label> <Label mb='x4'>{t('Department')}:</Label>
<Select flexShrink={0} options={depOptions} value={department} onChange={handleDepartment} placeholder={t('Department')}/> <AutoCompleteDepartment value={department} onChange={handleDepartment}/>
</Box> </Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'> <Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Status')}:</Label> <Label mb='x4'>{t('Status')}:</Label>
@ -145,6 +165,12 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
<Tags value={tags} handler={handleTags} /> <Tags value={tags} handler={handleTags} />
</Box> </Box>
</Box>} </Box>}
{allCustomFields && <Box display='flex' flexDirection='row' marginBlockStart='x8' {...props}>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Custom_fields')}:</Label>
<MultiSelect options={customFieldsOptions} value={customFields} onChange={handleCustomFields} flexGrow={1} {...props}/>
</Box>
</Box>}
</Box>; </Box>;
}; };
@ -156,7 +182,6 @@ function CurrentChatsPage({
params, params,
title, title,
renderRow, renderRow,
departments,
reload, reload,
children, children,
}) { }) {
@ -164,7 +189,7 @@ function CurrentChatsPage({
<Page> <Page>
<Page.Header title={title} /> <Page.Header title={title} />
<Page.Content> <Page.Content>
<GenericTable FilterComponent={FilterByText} header={header} renderRow={renderRow} results={data && data.rooms} departments={departments} total={data && data.total} setParams={setParams} params={params} reload={reload}/> <GenericTable FilterComponent={FilterByText} header={header} renderRow={renderRow} results={data && data.rooms} total={data && data.total} setParams={setParams} params={params} reload={reload}/>
</Page.Content> </Page.Content>
</Page> </Page>
{children} {children}

@ -2,7 +2,7 @@
import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState } from 'react'; import React, { useMemo, useCallback, useState } from 'react';
import { Table, Icon } from '@rocket.chat/fuselage'; import { Table, Icon, Button } from '@rocket.chat/fuselage';
import moment from 'moment'; import moment from 'moment';
import { FlowRouter } from 'meteor/kadira:flow-router'; import { FlowRouter } from 'meteor/kadira:flow-router';
@ -13,35 +13,54 @@ import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperime
import { useMethod } from '../../contexts/ServerContext'; import { useMethod } from '../../contexts/ServerContext';
import { usePermission } from '../../contexts/AuthorizationContext'; import { usePermission } from '../../contexts/AuthorizationContext';
import NotAuthorizedPage from '../../components/NotAuthorizedPage'; import NotAuthorizedPage from '../../components/NotAuthorizedPage';
import { useRoute } from '../../contexts/RouterContext';
import CurrentChatsPage from './CurrentChatsPage'; import CurrentChatsPage from './CurrentChatsPage';
import DeleteWarningModal from '../DeleteWarningModal';
import { useSetModal } from '../../contexts/ModalContext';
import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
export function RemoveChatButton({ _id, reload }) {
const removeChat = useMethod('livechat:removeRoom');
const setModal = useSetModal();
const dispatchToastMessage = useToastMessageDispatch();
const t = useTranslation();
export function RemoveCurrentChatButton({ _id, reload }) { const handleRemoveClick = useMutableCallback(async () => {
const removeCurrentChat = useMethod('livechat:removeCurrentChat');
const currentChatsRoute = useRoute('omnichannel-currentChats');
const handleRemoveClick = useMutableCallback(async (e) => {
e.preventDefault();
e.stopPropagation();
try { try {
await removeCurrentChat(_id); await removeChat(_id);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
currentChatsRoute.push({});
reload(); reload();
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; const handleDelete = useMutableCallback((e) => {
e.stopPropagation();
const onDeleteAgent = async () => {
try {
await handleRemoveClick();
dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
}
setModal();
};
setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
});
return <Table.Cell fontScale='p1' color='hint'withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);
const useQuery = ({ guest, servedBy, department, status, from, to, tags, itemsPerPage, current }, [column, direction]) => useMemo(() => { const useQuery = ({ guest, servedBy, department, status, from, to, tags, customFields, itemsPerPage, current }, [column, direction]) => useMemo(() => {
const query = { const query = {
roomName: guest, ...guest && { roomName: guest },
sort: JSON.stringify({ [column]: sortDir(direction), usernames: column === 'name' ? sortDir(direction) : undefined }), sort: JSON.stringify({ [column]: sortDir(direction), ts: column === 'ts' ? sortDir(direction) : undefined }),
...itemsPerPage && { count: itemsPerPage }, ...itemsPerPage && { count: itemsPerPage },
...current && { offset: current }, ...current && { offset: current },
}; };
@ -52,27 +71,30 @@ const useQuery = ({ guest, servedBy, department, status, from, to, tags, itemsPe
if (status !== 'all') { if (status !== 'all') {
query.open = status === 'open'; query.open = status === 'open';
} }
if (servedBy && servedBy.length > 0) { if (servedBy && servedBy !== 'all') {
query.agents = servedBy; query.agents = [servedBy];
} }
if (department && department.length > 0) { if (department && department !== 'all') {
if (department !== 'all') {
query.departmentId = department; query.departmentId = department;
} }
}
if (tags && tags.length > 0) { if (tags && tags.length > 0) {
query.tags = tags; query.tags = tags;
} }
if (customFields && Object.keys(customFields).length > 0) {
query.customFields = JSON.stringify(customFields);
}
return query; return query;
}, [guest, column, direction, itemsPerPage, current, from, to, status, servedBy, department, tags]); }, [guest, column, direction, itemsPerPage, current, from, to, status, servedBy, department, tags, customFields]);
function CurrentChatsRoute() { function CurrentChatsRoute() {
const t = useTranslation(); const t = useTranslation();
const canViewCurrentChats = usePermission('view-livechat-current-chats'); const canViewCurrentChats = usePermission('view-livechat-current-chats');
const [params, setParams] = useState({ fname: '', servedBy: [], status: '', department: '', from: '', to: '', current: 0, itemsPerPage: 25 }); const [params, setParams] = useState({ fname: '', servedBy: [], status: '', department: '', from: '', to: '', customFields: {}, current: 0, itemsPerPage: 25 });
const [sort, setSort] = useState(['name', 'asc']); const [sort, setSort] = useState(['ts', 'desc']);
const debouncedParams = useDebouncedValue(params, 500); const debouncedParams = useDebouncedValue(params, 500);
const debouncedSort = useDebouncedValue(sort, 500); const debouncedSort = useDebouncedValue(sort, 500);
@ -96,15 +118,15 @@ function CurrentChatsRoute() {
}); });
const { data, reload } = useEndpointDataExperimental('livechat/rooms', query) || {}; const { data, reload } = useEndpointDataExperimental('livechat/rooms', query) || {};
const { data: departments } = useEndpointDataExperimental('livechat/department', query) || {};
const header = useMemo(() => [ const header = useMemo(() => [
<Th key={'name'} direction={sort[1]} active={sort[0] === 'name'} onClick={onHeaderClick} sort='name' w='x120'>{t('Name')}</Th>, <Th key={'name'} direction={sort[1]} active={sort[0] === 'name'} onClick={onHeaderClick} sort='name' w='x120'>{t('Name')}</Th>,
<Th key={'departmentId'} direction={sort[1]} active={sort[0] === 'departmentId'} onClick={onHeaderClick} sort='departmentId' w='x200'>{t('Department')}</Th>, <Th key={'departmentId'} direction={sort[1]} active={sort[0] === 'departmentId'} onClick={onHeaderClick} sort='departmentId' w='x200'>{t('Department')}</Th>,
<Th key={'servedBy'} direction={sort[1]} active={sort[0] === 'servedBy'} onClick={onHeaderClick} sort='servedBy' w='x120'>{t('Served_by')}</Th>, <Th key={'servedBy'} direction={sort[1]} active={sort[0] === 'servedBy'} onClick={onHeaderClick} sort='servedBy' w='x120'>{t('Served_By')}</Th>,
<Th key={'ts'} direction={sort[1]} active={sort[0] === 'ts'} onClick={onHeaderClick} sort='ts' w='x120'>{t('Started_at')}</Th>, <Th key={'ts'} direction={sort[1]} active={sort[0] === 'ts'} onClick={onHeaderClick} sort='ts' w='x120'>{t('Started_At')}</Th>,
<Th key={'lm'} direction={sort[1]} active={sort[0] === 'lm'} onClick={onHeaderClick} sort='visibility' w='x120'>{t('Last_message')}</Th>, <Th key={'lm'} direction={sort[1]} active={sort[0] === 'lm'} onClick={onHeaderClick} sort='visibility' w='x120'>{t('Last_Message')}</Th>,
<Th key={'status'} direction={sort[1]} active={sort[0] === 'status'} onClick={onHeaderClick} sort='status' w='x120'>{t('Status')}</Th>, <Th key={'status'} direction={sort[1]} active={sort[0] === 'status'} onClick={onHeaderClick} sort='status' w='x120'>{t('Status')}</Th>,
<Th key={'remove'} w='x40'>{t('Remove')}</Th>,
].filter(Boolean), [sort, onHeaderClick, t]); ].filter(Boolean), [sort, onHeaderClick, t]);
const renderRow = useCallback(({ _id, fname, servedBy, ts, lm, department, open }) => <Table.Row key={_id} tabIndex={0} role='link' onClick={() => onRowClick(_id)} action qa-user-id={_id}> const renderRow = useCallback(({ _id, fname, servedBy, ts, lm, department, open }) => <Table.Row key={_id} tabIndex={0} role='link' onClick={() => onRowClick(_id)} action qa-user-id={_id}>
@ -114,7 +136,8 @@ function CurrentChatsRoute() {
<Table.Cell withTruncatedText>{moment(ts).format('L LTS')}</Table.Cell> <Table.Cell withTruncatedText>{moment(ts).format('L LTS')}</Table.Cell>
<Table.Cell withTruncatedText>{moment(lm).format('L LTS')}</Table.Cell> <Table.Cell withTruncatedText>{moment(lm).format('L LTS')}</Table.Cell>
<Table.Cell withTruncatedText>{open ? t('Open') : t('Closed')}</Table.Cell> <Table.Cell withTruncatedText>{open ? t('Open') : t('Closed')}</Table.Cell>
</Table.Row>, [onRowClick, t]); {!open && <RemoveChatButton _id={_id} reload={reload}/>}
</Table.Row>, [onRowClick, reload, t]);
if (!canViewCurrentChats) { if (!canViewCurrentChats) {
return <NotAuthorizedPage />; return <NotAuthorizedPage />;
@ -129,7 +152,6 @@ function CurrentChatsRoute() {
reload={reload} reload={reload}
header={header} header={header}
renderRow={renderRow} renderRow={renderRow}
departments={departments}
title={'Current Chats'}> title={'Current Chats'}>
</CurrentChatsPage>; </CurrentChatsPage>;
} }

@ -1,4 +1,4 @@
import { Icon, Table, Callout } from '@rocket.chat/fuselage'; import { Icon, Table, Callout, Button } from '@rocket.chat/fuselage';
import React, { useState, memo } from 'react'; import React, { useState, memo } from 'react';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
@ -43,7 +43,11 @@ export function RemoveCustomFieldButton({ _id, reload }) {
setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>); setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }

@ -1,7 +1,7 @@
import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState } from 'react'; import React, { useMemo, useCallback, useState } from 'react';
import { Box, Table, Icon } from '@rocket.chat/fuselage'; import { Box, Table, Icon, Button } from '@rocket.chat/fuselage';
import { Th } from '../../components/GenericTable'; import { Th } from '../../components/GenericTable';
import { useTranslation } from '../../contexts/TranslationContext'; import { useTranslation } from '../../contexts/TranslationContext';
@ -13,6 +13,7 @@ import ManagersPage from './ManagersPage';
import UserAvatar from '../../components/basic/avatar/UserAvatar'; import UserAvatar from '../../components/basic/avatar/UserAvatar';
export function RemoveManagerButton({ _id, reload }) { export function RemoveManagerButton({ _id, reload }) {
const t = useTranslation();
const deleteAction = useEndpointAction('DELETE', `livechat/users/manager/${ _id }`); const deleteAction = useEndpointAction('DELETE', `livechat/users/manager/${ _id }`);
const handleRemoveClick = useMutableCallback(async () => { const handleRemoveClick = useMutableCallback(async () => {
@ -21,7 +22,11 @@ export function RemoveManagerButton({ _id, reload }) {
reload(); reload();
} }
}); });
return <Table.Cell fontScale='p1' clickable={true} color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' clickable={true} color='hint' onClick={handleRemoveClick} withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleRemoveClick}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);

@ -1,5 +1,5 @@
import { Table, Callout, Icon, Button } from '@rocket.chat/fuselage'; import { Table, Callout, Icon } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useMutableCallback, Button } from '@rocket.chat/fuselage-hooks';
import React, { useState, memo, useMemo } from 'react'; import React, { useState, memo, useMemo } from 'react';
import GenericTable from '../../components/GenericTable'; import GenericTable from '../../components/GenericTable';
@ -79,8 +79,8 @@ const TriggersRow = memo(function TriggersRow(props) {
{enabled ? t('Yes') : t('No')} {enabled ? t('Yes') : t('No')}
</Table.Cell> </Table.Cell>
<Table.Cell withTruncatedText> <Table.Cell withTruncatedText>
<Button onClick={handleDelete} small> <Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16' /> <Icon name='trash' size='x16'/>
</Button> </Button>
</Table.Cell> </Table.Cell>
</Table.Row>; </Table.Row>;
@ -131,7 +131,10 @@ export function TriggersTable({ triggers, totalTriggers, params, onChangeParams,
<GenericTable.HeaderCell> <GenericTable.HeaderCell>
{t('Enabled')} {t('Enabled')}
</GenericTable.HeaderCell> </GenericTable.HeaderCell>
<GenericTable.HeaderCell width='x60'/> <GenericTable.HeaderCell width='x60'>
{t('Remove')}
</GenericTable.HeaderCell>
</>} </>}
results={triggers} results={triggers}
total={totalTriggers} total={totalTriggers}

@ -1,4 +1,4 @@
import { Table, Callout, Box, TextInput, Icon } from '@rocket.chat/fuselage'; import { Table, Callout, Box, TextInput, Icon, Button } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useState, memo, useMemo, useEffect } from 'react'; import React, { useState, memo, useMemo, useEffect } from 'react';
@ -43,7 +43,11 @@ export function RemoveBusinessHourButton({ _id, type, reload }) {
setModal(<DeleteWarningModal onDelete={onBusinessHour} onCancel={() => setModal()}/>); setModal(<DeleteWarningModal onDelete={onBusinessHour} onCancel={() => setModal()}/>);
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
const FilterByText = memo(({ setFilter, ...props }) => { const FilterByText = memo(({ setFilter, ...props }) => {

@ -2,7 +2,7 @@
import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState } from 'react'; import React, { useMemo, useCallback, useState } from 'react';
import { Table, Icon } from '@rocket.chat/fuselage'; import { Table, Icon, Button } from '@rocket.chat/fuselage';
import { Th } from '../../../../client/components/GenericTable'; import { Th } from '../../../../client/components/GenericTable';
import { useTranslation } from '../../../../client/contexts/TranslationContext'; import { useTranslation } from '../../../../client/contexts/TranslationContext';
@ -49,7 +49,11 @@ export function RemovePriorityButton({ _id, reload }) {
setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>); setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);

@ -2,7 +2,7 @@
import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState } from 'react'; import React, { useMemo, useCallback, useState } from 'react';
import { Table, Icon } from '@rocket.chat/fuselage'; import { Table, Icon, Button } from '@rocket.chat/fuselage';
import { Th } from '../../../../client/components/GenericTable'; import { Th } from '../../../../client/components/GenericTable';
import { useTranslation } from '../../../../client/contexts/TranslationContext'; import { useTranslation } from '../../../../client/contexts/TranslationContext';
@ -50,7 +50,11 @@ export function RemoveTagButton({ _id, reload }) {
setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>); setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);

@ -2,7 +2,7 @@
import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState } from 'react'; import React, { useMemo, useCallback, useState } from 'react';
import { Table, Icon } from '@rocket.chat/fuselage'; import { Table, Icon, Button } from '@rocket.chat/fuselage';
import { Th } from '../../../../client/components/GenericTable'; import { Th } from '../../../../client/components/GenericTable';
import { useTranslation } from '../../../../client/contexts/TranslationContext'; import { useTranslation } from '../../../../client/contexts/TranslationContext';
@ -51,7 +51,11 @@ export function RemoveUnitButton({ _id, reload }) {
setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>); setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
}); });
return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>; return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
<Button small ghost title={t('Remove')} onClick={handleDelete}>
<Icon name='trash' size='x16'/>
</Button>
</Table.Cell>;
} }
const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1); const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);

@ -3215,6 +3215,8 @@
"Send_request_on": "Send Request on", "Send_request_on": "Send Request on",
"Agent_messages": "Agent Messages", "Agent_messages": "Agent Messages",
"Chat_close": "Chat Close", "Chat_close": "Chat Close",
"Chats_removed": "Chats Removed",
"Chat_removed": "Chat Removed",
"Chat_queued": "Chat Queued", "Chat_queued": "Chat Queued",
"Chat_start": "Chat Start", "Chat_start": "Chat Start",
"Chat_taken": "Chat Taken", "Chat_taken": "Chat Taken",

Loading…
Cancel
Save