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
4 changes: 2 additions & 2 deletions app/livechat/client/lib/chartHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ export const drawDoughnutChart = async (chart, title, chartContext, dataLabels,
data: dataPoints, // data points corresponding to data labels, x-axis points
backgroundColor: [
'#2de0a5',
'#ffd21f',
'#f5455c',
'#cbced1',
'#f5455c',
'#ffd21f',
],
borderWidth: 0,
}],
Expand Down
4 changes: 3 additions & 1 deletion app/livechat/imports/server/rest/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ API.v1.addRoute('livechat/rooms', { authRequired: true }, {
get() {
const { offset, count } = this.getPaginationItems();
const { sort, fields } = this.parseJsonQuery();
const { agents, departmentId, open, tags, roomName } = this.requestParams();
const { agents, departmentId, open, tags, roomName, onhold } = this.requestParams();
let { createdAt, customFields, closedAt } = this.requestParams();
check(agents, Match.Maybe([String]));
check(roomName, Match.Maybe(String));
check(departmentId, Match.Maybe(String));
check(open, Match.Maybe(String));
check(onhold, Match.Maybe(String));
check(tags, Match.Maybe([String]));

const hasAdminAccess = hasPermission(this.userId, 'view-livechat-rooms');
Expand All @@ -51,6 +52,7 @@ API.v1.addRoute('livechat/rooms', { authRequired: true }, {
closedAt,
tags,
customFields,
onhold,
options: { offset, count, sort, fields },
})));
},
Expand Down
2 changes: 2 additions & 0 deletions app/livechat/server/api/lib/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export async function findRooms({
closedAt,
tags,
customFields,
onhold,
options: {
offset,
count,
Expand All @@ -25,6 +26,7 @@ export async function findRooms({
closedAt,
tags,
customFields,
onhold: ['t', 'true', '1'].includes(onhold),
options: {
sort: sort || { ts: -1 },
offset,
Expand Down
13 changes: 8 additions & 5 deletions app/livechat/server/lib/Analytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import moment from 'moment';

import { LivechatRooms } from '../../../models';
import { LivechatRooms as LivechatRoomsRaw } from '../../../models/server/raw';
import { secondsToHHMMSS } from '../../../utils/server';
import { getTimezone } from '../../../utils/server/lib/getTimezone';
import { Logger } from '../../../logger';
Expand Down Expand Up @@ -288,8 +289,8 @@ export const Analytics = {
const totalMessagesInHour = new Map(); // total messages in hour 0, 1, ... 23 of weekday
const days = to.diff(from, 'days') + 1; // total days

const summarize = (m) => ({ metrics, msgs }) => {
if (metrics && !metrics.chatDuration) {
const summarize = (m) => ({ metrics, msgs, onHold = false }) => {
if (metrics && !metrics.chatDuration && !onHold) {
openConversations++;
}
totalMessages += msgs;
Expand Down Expand Up @@ -337,13 +338,17 @@ export const Analytics = {
to: utcBusiestHour >= 0 ? moment.utc().set({ hour: utcBusiestHour }).tz(timezone).format('hA') : '-',
from: utcBusiestHour >= 0 ? moment.utc().set({ hour: utcBusiestHour }).subtract(1, 'hour').tz(timezone).format('hA') : '',
};
const onHoldConversations = Promise.await(LivechatRoomsRaw.getOnHoldConversationsBetweenDate(from, to, departmentId));

const data = [{
return [{
title: 'Total_conversations',
value: totalConversations,
}, {
title: 'Open_conversations',
value: openConversations,
}, {
title: 'On_Hold_conversations',
value: onHoldConversations,
}, {
title: 'Total_messages',
value: totalMessages,
Expand All @@ -357,8 +362,6 @@ export const Analytics = {
title: 'Busiest_time',
value: `${ busiestHour.from }${ busiestHour.to ? `- ${ busiestHour.to }` : '' }`,
}];

return data;
},

/**
Expand Down
12 changes: 10 additions & 2 deletions app/livechat/server/lib/analytics/dashboards.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const findAllChatsStatusAsync = async ({
open: await LivechatRooms.countAllOpenChatsBetweenDate({ start, end, departmentId }),
closed: await LivechatRooms.countAllClosedChatsBetweenDate({ start, end, departmentId }),
queued: await LivechatRooms.countAllQueuedChatsBetweenDate({ start, end, departmentId }),
onhold: await LivechatRooms.getOnHoldConversationsBetweenDate(start, end, departmentId),
};
};

Expand Down Expand Up @@ -193,7 +194,7 @@ const getConversationsMetricsAsync = async ({
utcOffset: user.utcOffset,
language: user.language || settings.get('Language') || 'en',
});
const metrics = ['Total_conversations', 'Open_conversations', 'Total_messages'];
const metrics = ['Total_conversations', 'Open_conversations', 'On_Hold_conversations', 'Total_messages'];
const visitorsCount = await LivechatVisitors.getVisitorsBetweenDate({ start, end, department: departmentId }).count();
return {
totalizers: [
Expand All @@ -213,13 +214,20 @@ const findAllChatMetricsByAgentAsync = async ({
}
const open = await LivechatRooms.countAllOpenChatsByAgentBetweenDate({ start, end, departmentId });
const closed = await LivechatRooms.countAllClosedChatsByAgentBetweenDate({ start, end, departmentId });
const onhold = await LivechatRooms.countAllOnHoldChatsByAgentBetweenDate({ start, end, departmentId });
const result = {};
(open || []).forEach((agent) => {
result[agent._id] = { open: agent.chats, closed: 0 };
result[agent._id] = { open: agent.chats, closed: 0, onhold: 0 };
});
(closed || []).forEach((agent) => {
result[agent._id] = { open: result[agent._id] ? result[agent._id].open : 0, closed: agent.chats };
});
(onhold || []).forEach((agent) => {
result[agent._id] = {
...result[agent._id],
onhold: agent.chats,
};
});
return result;
};

Expand Down
2 changes: 2 additions & 0 deletions app/models/server/models/LivechatRooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ export class LivechatRooms extends Base {
open: '$open',
servedBy: '$servedBy',
metrics: '$metrics',
onHold: '$onHold',
},
messagesCount: {
$sum: 1,
Expand All @@ -578,6 +579,7 @@ export class LivechatRooms extends Base {
servedBy: '$_id.servedBy',
metrics: '$_id.metrics',
msgs: '$messagesCount',
onHold: '$_id.onHold',
},
},
]);
Expand Down
76 changes: 74 additions & 2 deletions app/models/server/raw/LivechatRooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,16 @@ export class LivechatRoomsRaw extends BaseRaw {
'metrics.chatDuration': {
$exists: false,
},
$or: [{
onHold: {
$exists: false,
},
}, {
onHold: {
$exists: true,
$eq: false,
},
}],
servedBy: { $exists: true },
ts: { $gte: new Date(start), $lte: new Date(end) },
};
Expand All @@ -494,7 +504,6 @@ export class LivechatRoomsRaw extends BaseRaw {
'metrics.chatDuration': {
$exists: true,
},
servedBy: { $exists: true },
ts: { $gte: new Date(start), $lte: new Date(end) },
};
if (departmentId && departmentId !== 'undefined') {
Expand All @@ -507,6 +516,7 @@ export class LivechatRoomsRaw extends BaseRaw {
const query = {
t: 'l',
servedBy: { $exists: false },
open: true,
ts: { $gte: new Date(start), $lte: new Date(end) },
};
if (departmentId && departmentId !== 'undefined') {
Expand All @@ -521,6 +531,41 @@ export class LivechatRoomsRaw extends BaseRaw {
t: 'l',
'servedBy.username': { $exists: true },
open: true,
$or: [{
onHold: {
$exists: false,
},
}, {
onHold: {
$exists: true,
$eq: false,
},
}],
ts: { $gte: new Date(start), $lte: new Date(end) },
},
};
const group = {
$group: {
_id: '$servedBy.username',
chats: { $sum: 1 },
},
};
if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
return this.col.aggregate([match, group]).toArray();
}

countAllOnHoldChatsByAgentBetweenDate({ start, end, departmentId }) {
const match = {
$match: {
t: 'l',
'servedBy.username': { $exists: true },
open: true,
onHold: {
$exists: true,
$eq: true,
},
ts: { $gte: new Date(start), $lte: new Date(end) },
},
};
Expand Down Expand Up @@ -896,7 +941,7 @@ export class LivechatRoomsRaw extends BaseRaw {
return this.col.aggregate(params);
}

findRoomsWithCriteria({ agents, roomName, departmentId, open, served, createdAt, closedAt, tags, customFields, visitorId, roomIds, options = {} }) {
findRoomsWithCriteria({ agents, roomName, departmentId, open, served, createdAt, closedAt, tags, customFields, visitorId, roomIds, onhold, options = {} }) {
const query = {
t: 'l',
};
Expand All @@ -911,6 +956,7 @@ export class LivechatRoomsRaw extends BaseRaw {
}
if (open !== undefined) {
query.open = { $exists: open };
query.onHold = { $ne: true };
}
if (served !== undefined) {
query.servedBy = { $exists: served };
Expand Down Expand Up @@ -947,9 +993,35 @@ export class LivechatRoomsRaw extends BaseRaw {
query._id = { $in: roomIds };
}

if (onhold) {
query.onHold = {
$exists: true,
$eq: onhold,
};
}

return this.find(query, { sort: options.sort || { name: 1 }, skip: options.offset, limit: options.count });
}

getOnHoldConversationsBetweenDate(from, to, departmentId) {
const query = {
onHold: {
$exists: true,
$eq: true,
},
ts: {
$gte: new Date(from), // ISO Date, ts >= date.gte
$lt: new Date(to), // ISODate, ts < date.lt
},
};

if (departmentId && departmentId !== 'undefined') {
query.departmentId = departmentId;
}

return this.find(query).count();
}

findAllServiceTimeByAgent({ start, end, onlyCount = false, options = {} }) {
const match = {
$match: {
Expand Down
50 changes: 30 additions & 20 deletions client/views/omnichannel/currentChats/CurrentChatsRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const useQuery: useQueryType = (
departmentId?: string;
tags?: string[];
customFields?: string;
onhold?: boolean;
} = {
...(guest && { roomName: guest }),
sort: JSON.stringify({
Expand All @@ -71,8 +72,10 @@ const useQuery: useQueryType = (
}),
});
}

if (status !== 'all') {
query.open = status === 'opened';
query.open = status === 'opened' || status === 'onhold';
query.onhold = status === 'onhold';
}
if (servedBy && servedBy !== 'all') {
query.agents = [servedBy];
Expand Down Expand Up @@ -215,25 +218,32 @@ const CurrentChatsRoute: FC = () => {
);

const renderRow = useCallback(
({ _id, fname, servedBy, ts, lm, department, open }) => (
<Table.Row
key={_id}
tabIndex={0}
role='link'
onClick={(): void => onRowClick(_id)}
action
qa-user-id={_id}
>
<Table.Cell withTruncatedText>{fname}</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>
{canRemoveClosedChats && !open && <RemoveChatButton _id={_id} reload={reload} />}
</Table.Row>
),
[onRowClick, reload, t, canRemoveClosedChats],
({ _id, fname, servedBy, ts, lm, department, open, onHold }) => {
const getStatusText = (open: boolean, onHold: boolean): string => {
if (!open) return t('Closed');
return onHold ? t('On_Hold_Chats') : t('Open');
};

return (
<Table.Row
key={_id}
tabIndex={0}
role='link'
onClick={(): void => onRowClick(_id)}
action
qa-user-id={_id}
>
<Table.Cell withTruncatedText>{fname}</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>{getStatusText(open, onHold)}</Table.Cell>
{canRemoveClosedChats && !open && <RemoveChatButton _id={_id} reload={reload} />}
</Table.Row>
);
},
[onRowClick, reload, canRemoveClosedChats, t],
);

if (!canViewCurrentChats) {
Expand Down
3 changes: 2 additions & 1 deletion client/views/omnichannel/currentChats/FilterByText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const FilterByText: FilterByTextType = ({ setFilter, reload, ...props }) => {
['all', t('All')],
['closed', t('Closed')],
['opened', t('Open')],
['onhold', t('On_Hold_Chats')],
];
const customFieldsOptions: [string, string][] = useMemo(
() =>
Expand Down Expand Up @@ -110,7 +111,7 @@ const FilterByText: FilterByTextType = ({ setFilter, reload, ...props }) => {
reload && reload();
dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
} catch (error) {
dispatchToastMessage({ type: 'error', message: error });
dispatchToastMessage({ type: 'error', message: (error as Error).message });
}
setModal(null);
};
Expand Down
Loading