Skip to content

Commit

Permalink
Current Chats wip
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinSchoeler committed Aug 21, 2020
1 parent 3aca1b0 commit 2cfbafb
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 9 deletions.
8 changes: 0 additions & 8 deletions app/livechat/client/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ AccountBox.addRoute({
pageTemplate: 'livechatDashboard',
}, livechatManagerRoutes, load);

AccountBox.addRoute({
name: 'livechat-current-chats',
path: '/current',
sideNav: 'omnichannelFlex',
i18nPageTitle: 'Current_Chats',
pageTemplate: 'livechatCurrentChats',
}, livechatManagerRoutes, load);

AccountBox.addRoute({
name: 'livechat-analytics',
path: '/analytics',
Expand Down
3 changes: 2 additions & 1 deletion client/components/GenericTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const GenericTable = forwardRef(function GenericTable({
setParams = () => { },
params: paramsDefault = '',
FilterComponent = () => null,
...props
}, ref) {
const t = useTranslation();

Expand All @@ -70,7 +71,7 @@ export const GenericTable = forwardRef(function GenericTable({
const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), [t]);

return <>
<FilterComponent setFilter={setFilter} />
<FilterComponent setFilter={setFilter} { ...props}/>
{results && !results.length
? <Tile fontScale='p1' elevation='0' color='info' textAlign='center'>
{t('No_data_found')}
Expand Down
149 changes: 149 additions & 0 deletions client/omnichannel/currentChats/CurrentChatsPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import React, { useEffect, useMemo } from 'react';
import { TextInput, Box, Icon, MultiSelect, Select, InputBox, Menu } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import moment from 'moment';

import Page from '../../components/basic/Page';
import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
import { usePermission } from '../../contexts/AuthorizationContext';
import { GenericTable } from '../../components/GenericTable';
import { useForm } from '../../hooks/useForm';
import { useMethod } from '../../contexts/ServerContext';


// 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 RemoveAllClosed = ({ handleClearFilters, handleRemoveClosed, ...props }) => {
const t = useTranslation();
const canRemove = usePermission('remove-closed-livechat-rooms');

const menuOptions = {
clearFilters: {
label: <Box>
<Icon name='refresh' size='x16' marginInlineEnd='x4' />{t('Clear_filters')}
</Box>,
action: handleClearFilters,
},
...canRemove && {
removeClosed: {
label: <Box color='danger'>
<Icon name='trash' size='x16' marginInlineEnd='x4' />{t('Remove')}
</Box>,
action: handleRemoveClosed,
},
},
};
return <Menu options={menuOptions} {...props}/>;
};


const FilterByText = ({ setFilter, reload, ...props }) => {
const t = useTranslation();

const { data: departments } = useEndpointDataExperimental('livechat/department') || {};
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 { values, handlers, reset } = useForm({ guest: '', servedBy: [], status: 'all', department: undefined, from: '', to: '' });
const {
handleGuest,
handleServedBy,
handleStatus,
handleDepartment,
handleFrom,
handleTo,
} = handlers;
const {
guest,
servedBy,
status,
department,
from,
to,
} = values;

const onSubmit = useMutableCallback((e) => e.preventDefault());

useEffect(() => {
setFilter({
guest,
servedBy,
status,
department,
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'),
});
console.log(servedBy);
}, [setFilter, guest, servedBy, status, department, from, to]);

const handleClearFilters = useMutableCallback(() => {
reset();
});

const removeClosedChats = useMethod('livechat:removeAllClosedRooms');

const handleRemoveClosed = useMutableCallback(async () => {
await removeClosedChats();
console.log(props);
reload();
});

return <Box mb='x16' is='form' onSubmit={onSubmit} display='flex' flexDirection='row' {...props}>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4' >{t('Guest')}:</Label>
<TextInput flexShrink={0} placeholder={t('Guest')} onChange={handleGuest} value={guest} />
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Served_By')}:</Label>
<MultiSelect flexShrink={0} options={agentOptions} value={servedBy} onChange={handleServedBy} placeholder={t('Served_By')}/>
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Department')}:</Label>
<Select flexShrink={0} options={depOptions} value={department} onChange={handleDepartment} placeholder={t('Department')}/>
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('Status')}:</Label>
<Select flexShrink={0} options={statusOptions} value={status} onChange={handleStatus} placeholder={t('Status')}/>
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('From')}:</Label>
<InputBox type='date' flexShrink={0} placeholder={t('From')} onChange={handleFrom} value={from} />
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Label mb='x4'>{t('To')}:</Label>
<InputBox type='date' flexShrink={0} placeholder={t('To')} onChange={handleTo} value={to} />
</Box>
<RemoveAllClosed handleClearFilters={handleClearFilters} handleRemoveClosed={handleRemoveClosed}/>
</Box>;
};


function CurrentChatsPage({
data,
header,
setParams,
params,
title,
renderRow,
departments,
reload,
children,
}) {
return <Page flexDirection='row'>
<Page>
<Page.Header title={title} />
<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}/>
</Page.Content>
</Page>
{children}
</Page>;
}

export default CurrentChatsPage;
137 changes: 137 additions & 0 deletions client/omnichannel/currentChats/CurrentChatsRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@


import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useCallback, useState, useEffect } from 'react';
import { Table, Icon } from '@rocket.chat/fuselage';
import moment from 'moment';
import { FlowRouter } from 'meteor/kadira:flow-router';


import { Th } from '../../components/GenericTable';
import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
import { useMethod } from '../../contexts/ServerContext';
import { usePermission } from '../../contexts/AuthorizationContext';
import NotAuthorizedPage from '../../components/NotAuthorizedPage';
import { useRouteParameter, useRoute } from '../../contexts/RouterContext';
import { useForm } from '../../hooks/useForm';
import CurrentChatsPage from './CurrentChatsPage';

export function RemoveCurrentChatButton({ _id, reload }) {
const removeCurrentChat = useMethod('livechat:removeCurrentChat');
const currentChatsRoute = useRoute('omnichannel-currentChats');


const handleRemoveClick = useMutableCallback(async (e) => {
e.preventDefault();
e.stopPropagation();
try {
await removeCurrentChat(_id);
} catch (error) {
console.log(error);
}
currentChatsRoute.push({});
reload();
});

return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
}

const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);

const useQuery = ({ guest, servedBy, department, status, from, to, itemsPerPage, current }, [column, direction]) => useMemo(() => {
const query = {
roomName: guest,
sort: JSON.stringify({ [column]: sortDir(direction), usernames: column === 'name' ? sortDir(direction) : undefined }),
...itemsPerPage && { count: itemsPerPage },
...current && { offset: current },
};

if (from && to) {
query.createdAt = JSON.stringify({ start: from, end: to });
}
if (status !== 'all') {
query.open = status === 'open';
}
if (servedBy && servedBy.length > 0) {
query.agents = servedBy;
}
if (department && department.length > 0) {
console.log(department);
query.departmentId = department;
}

console.log(query);
return query;
}, [guest, servedBy, department, from, to, status, column, direction, itemsPerPage, current]);

function CurrentChatsRoute() {
const t = useTranslation();
const canViewCurrentChats = usePermission('view-livechat-current-chats');

const [params, setParams] = useState({ fname: '', servedBy: [], status: '', department: '', from: '', to: '', current: 0, itemsPerPage: 25 });
const [sort, setSort] = useState(['name', 'asc']);

const debouncedParams = useDebouncedValue(params, 500);
const debouncedSort = useDebouncedValue(sort, 500);
const query = useQuery(debouncedParams, debouncedSort);
// const livechatRoomRoute = useRoute('live/:id');

const onHeaderClick = useMutableCallback((id) => {
const [sortBy, sortDirection] = sort;

if (sortBy === id) {
setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
return;
}
setSort([id, 'asc']);
});

const onRowClick = useMutableCallback((_id) => {
FlowRouter.go('live', { id: _id });
// routing this way causes a 404 that only goes away with a refresh, need to fix in review
// livechatRoomRoute.push({ id: _id });
});

const { data, reload } = useEndpointDataExperimental('livechat/rooms', query) || {};
const { data: departments } = useEndpointDataExperimental('livechat/department', query) || {};

console.log(data, departments);

const header = useMemo(() => [
<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={'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={'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>,
].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}>
<Table.Cell withTruncatedText>{fname}{_id}</Table.Cell>
<Table.Cell withTruncatedText>{department ? department.name : ''}</Table.Cell>
<Table.Cell withTruncatedText>{servedBy && servedBy.username}</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>{open ? t('Open') : t('Closed')}</Table.Cell>
</Table.Row>, [onRowClick, t]);

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


return <CurrentChatsPage
setParams={setParams}
params={params}
onHeaderClick={onHeaderClick}
data={data} useQuery={useQuery}
reload={reload}
header={header}
renderRow={renderRow}
departments={departments}
title={'Current Chats'}>
</CurrentChatsPage>;
}

export default CurrentChatsRoute;
5 changes: 5 additions & 0 deletions client/omnichannel/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,8 @@ registerOmnichannelRoute('/facebook', {
name: 'omnichannel-facebook',
lazyRouteComponent: () => import('./facebook/FacebookPage'),
});

registerOmnichannelRoute('/current-chats', {
name: 'omnichannel-current-chats',
lazyRouteComponent: () => import('./currentChats/CurrentChatsRoute'),
});

0 comments on commit 2cfbafb

Please sign in to comment.