diff --git a/app/livechat/client/stylesheets/livechat.css b/app/livechat/client/stylesheets/livechat.css
index 3a13815dbe116..f80d646c0b5ca 100644
--- a/app/livechat/client/stylesheets/livechat.css
+++ b/app/livechat/client/stylesheets/livechat.css
@@ -220,8 +220,7 @@
.lc-analytics-table {
display: flex;
- height: ~"calc(100% - 280px)";
- height: ~"-webkit-calc-height(100% - 280px)";
+ height: 100%;
min-height: 300px;
flex-flow: column;
diff --git a/app/livechat/client/views/app/analytics/livechatAnalytics.html b/app/livechat/client/views/app/analytics/livechatAnalytics.html
index 6ae03f99cb5f8..35ace4bfd8014 100644
--- a/app/livechat/client/views/app/analytics/livechatAnalytics.html
+++ b/app/livechat/client/views/app/analytics/livechatAnalytics.html
@@ -10,6 +10,28 @@
+ {{#if hasDepartments }}
+
+ {{> livechatAutocompleteUser
+ onClickTag=onClickTagDepartment
+ list=selectedDepartments
+ onSelect=onSelectDepartments
+ collection='CachedDepartmentList'
+ endpoint='livechat/department.autocomplete'
+ field='name'
+ sort='name'
+ placeholder="Select_a_department"
+ name="department"
+ icon="queue"
+ noMatchTemplate="userSearchEmpty"
+ templateItem="popupList_item_channel"
+ template="roomSearch"
+ noMatchTemplate="roomSearchEmpty"
+ modifier=departmentModifier
+ }}
+
+ {{/if}}
+
+ {{#if hasDepartments }}
+
+ {{> livechatAutocompleteUser
+ onClickTag=onClickTagDepartment
+ list=selectedDepartments
+ onSelect=onSelectDepartments
+ collection='CachedDepartmentList'
+ endpoint='livechat/department.autocomplete'
+ field='name'
+ sort='name'
+ placeholder="Select_a_department"
+ name="department"
+ icon="queue"
+ noMatchTemplate="userSearchEmpty"
+ templateItem="popupList_item_channel"
+ template="roomSearch"
+ noMatchTemplate="roomSearchEmpty"
+ modifier=departmentModifier
+ }}
+
+ {{/if}}
{{#if isLoading}}
{{> loading }}
diff --git a/app/livechat/client/views/app/analytics/livechatRealTimeMonitoring.js b/app/livechat/client/views/app/analytics/livechatRealTimeMonitoring.js
index 153bb24440d74..584e0f003b1f2 100644
--- a/app/livechat/client/views/app/analytics/livechatRealTimeMonitoring.js
+++ b/app/livechat/client/views/app/analytics/livechatRealTimeMonitoring.js
@@ -102,6 +102,8 @@ const updateChartData = async (chartId, label, data) => {
let timer;
+const getChartDepartment = (department) => department?._id;
+
const getDaterange = () => {
const today = moment(new Date());
return {
@@ -110,8 +112,11 @@ const getDaterange = () => {
};
};
-const loadConversationOverview = async ({ start, end }) => {
- const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/conversation-totalizers?start=${ start }&end=${ end }`);
+const parseAdditionalParams = (options = {}, prefix = '') => `${ prefix }${ Object.keys(options).map((key) => `${ key }=${ options[key] }`).join('&') }`;
+
+const loadConversationOverview = async ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/conversation-totalizers?start=${ start }&end=${ end }${ additionalParams }`);
return totalizers;
};
@@ -121,8 +126,9 @@ const updateConversationOverview = async (totalizers) => {
}
};
-const loadAgentsOverview = async ({ start, end }) => {
- const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/agents-productivity-totalizers?start=${ start }&end=${ end }`);
+const loadAgentsOverview = async ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/agents-productivity-totalizers?start=${ start }&end=${ end }${ additionalParams }`);
return totalizers;
};
@@ -131,8 +137,9 @@ const updateAgentsOverview = async (totalizers) => {
templateInstance.agentsOverview.set(totalizers);
}
};
-const loadChatsOverview = async ({ start, end }) => {
- const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/chats-totalizers?start=${ start }&end=${ end }`);
+const loadChatsOverview = async ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/chats-totalizers?start=${ start }&end=${ end }${ additionalParams }`);
return totalizers;
};
@@ -142,8 +149,9 @@ const updateChatsOverview = async (totalizers) => {
}
};
-const loadProductivityOverview = async ({ start, end }) => {
- const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/productivity-totalizers?start=${ start }&end=${ end }`);
+const loadProductivityOverview = async ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ const { totalizers } = await APIClient.v1.get(`livechat/analytics/dashboards/productivity-totalizers?start=${ start }&end=${ end }${ additionalParams }`);
return totalizers;
};
@@ -153,7 +161,10 @@ const updateProductivityOverview = async (totalizers) => {
}
};
-const loadChatsChartData = ({ start, end }) => APIClient.v1.get(`livechat/analytics/dashboards/charts/chats?start=${ start }&end=${ end }`);
+const loadChatsChartData = ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ return APIClient.v1.get(`livechat/analytics/dashboards/charts/chats?start=${ start }&end=${ end }${ additionalParams }`);
+};
const updateChatsChart = async ({ open, closed, queued }) => {
await updateChartData('lc-chats-chart', 'Open', [open]);
@@ -161,19 +172,26 @@ const updateChatsChart = async ({ open, closed, queued }) => {
await updateChartData('lc-chats-chart', 'Queue', [queued]);
};
-const loadChatsPerAgentChartData = async ({ start, end }) => {
- const result = await APIClient.v1.get(`livechat/analytics/dashboards/charts/chats-per-agent?start=${ start }&end=${ end }`);
+const loadChatsPerAgentChartData = async ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ const result = await APIClient.v1.get(`livechat/analytics/dashboards/charts/chats-per-agent?start=${ start }&end=${ end }${ additionalParams }`);
delete result.success;
return result;
};
-const updateChatsPerAgentChart = (agents) => {
+const updateChatsPerAgentChart = async (agents) => {
+ // this chart need to reset before new updates
+ chartContexts['lc-chats-per-agent-chart'] = await initChart['lc-chats-per-agent-chart']();
+
Object
.keys(agents)
.forEach((agent) => updateChartData('lc-chats-per-agent-chart', agent, [agents[agent].open, agents[agent].closed]));
};
-const loadAgentsStatusChartData = () => APIClient.v1.get('livechat/analytics/dashboards/charts/agents-status');
+const loadAgentsStatusChartData = ({ departmentId }) => {
+ const additionalParams = parseAdditionalParams({ departmentId }, '?');
+ return APIClient.v1.get(`livechat/analytics/dashboards/charts/agents-status${ additionalParams }`);
+};
const updateAgentStatusChart = async (statusData) => {
if (!statusData) {
@@ -186,19 +204,26 @@ const updateAgentStatusChart = async (statusData) => {
await updateChartData('lc-agents-chart', 'Busy', [statusData.busy]);
};
-const loadChatsPerDepartmentChartData = async ({ start, end }) => {
- const result = await APIClient.v1.get(`livechat/analytics/dashboards/charts/chats-per-department?start=${ start }&end=${ end }`);
+const loadChatsPerDepartmentChartData = async ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ const result = await APIClient.v1.get(`livechat/analytics/dashboards/charts/chats-per-department?start=${ start }&end=${ end }${ additionalParams }`);
delete result.success;
return result;
};
-const updateDepartmentsChart = (departments) => {
+const updateDepartmentsChart = async (departments) => {
+ // this chart need to reset before new updates
+ chartContexts['lc-chats-per-dept-chart'] = await initChart['lc-chats-per-dept-chart']();
+
Object
.keys(departments)
.forEach((department) => updateChartData('lc-chats-per-dept-chart', department, [departments[department].open, departments[department].closed]));
};
-const loadTimingsChartData = ({ start, end }) => APIClient.v1.get(`livechat/analytics/dashboards/charts/timings?start=${ start }&end=${ end }`);
+const loadTimingsChartData = ({ start, end, ...options }) => {
+ const additionalParams = parseAdditionalParams(options, '&');
+ return APIClient.v1.get(`livechat/analytics/dashboards/charts/timings?start=${ start }&end=${ end }${ additionalParams }`);
+};
const updateTimingsChart = async (timingsData) => {
const hour = moment(new Date()).format('H');
@@ -229,9 +254,27 @@ Template.livechatRealTimeMonitoring.helpers({
isLoading() {
return Template.instance().isLoading.get();
},
+ departmentModifier() {
+ return (filter, text = '') => {
+ const f = filter.get();
+ return `${ f.length === 0 ? text : text.replace(new RegExp(filter.get(), 'i'), (part) => `${ part }`) }`;
+ };
+ },
+ onClickTagDepartment() {
+ return Template.instance().onClickTagDepartment;
+ },
+ selectedDepartments() {
+ return Template.instance().selectedDepartments.get();
+ },
+ onSelectDepartments() {
+ return Template.instance().onSelectDepartments;
+ },
+ hasDepartments() {
+ return Template.instance().hasDepartments.get();
+ },
});
-Template.livechatRealTimeMonitoring.onCreated(function() {
+Template.livechatRealTimeMonitoring.onCreated(async function() {
templateInstance = Template.instance();
this.isLoading = new ReactiveVar(false);
this.conversationsOverview = new ReactiveVar();
@@ -240,22 +283,43 @@ Template.livechatRealTimeMonitoring.onCreated(function() {
this.agentsOverview = new ReactiveVar();
this.conversationTotalizers = new ReactiveVar([]);
this.interval = new ReactiveVar(5);
+ this.selectedDepartments = new ReactiveVar([]);
+ this.hasDepartments = new ReactiveVar(false);
+
+ this.onSelectDepartments = ({ item: department }) => {
+ department.text = department.name;
+ this.selectedDepartments.set([department]);
+ };
+
+ this.onClickTagDepartment = () => {
+ this.selectedDepartments.set([]);
+ };
+
+ const { departments } = await APIClient.v1.get('livechat/department?count=1');
+ this.hasDepartments.set(departments?.length > 0);
});
Template.livechatRealTimeMonitoring.onRendered(async function() {
await initAllCharts();
this.updateDashboard = async () => {
+ const [department] = this.selectedDepartments.get();
+ const departmentId = getChartDepartment(department);
const daterange = getDaterange();
- updateConversationOverview(await loadConversationOverview(daterange));
- updateProductivityOverview(await loadProductivityOverview(daterange));
- updateChatsChart(await loadChatsChartData(daterange));
- updateChatsPerAgentChart(await loadChatsPerAgentChartData(daterange));
- updateAgentStatusChart(await loadAgentsStatusChartData());
- updateDepartmentsChart(await loadChatsPerDepartmentChartData(daterange));
- updateTimingsChart(await loadTimingsChartData(daterange));
- updateAgentsOverview(await loadAgentsOverview(daterange));
- updateChatsOverview(await loadChatsOverview(daterange));
+ const filters = Object.assign(
+ { ...daterange },
+ departmentId && { departmentId },
+ );
+
+ updateConversationOverview(await loadConversationOverview(filters));
+ updateProductivityOverview(await loadProductivityOverview(filters));
+ updateChatsChart(await loadChatsChartData(filters));
+ updateChatsPerAgentChart(await loadChatsPerAgentChartData(filters));
+ updateAgentStatusChart(await loadAgentsStatusChartData(filters));
+ updateDepartmentsChart(await loadChatsPerDepartmentChartData(filters));
+ updateTimingsChart(await loadTimingsChartData(filters));
+ updateAgentsOverview(await loadAgentsOverview(filters));
+ updateChatsOverview(await loadChatsOverview(filters));
};
this.autorun(() => {
if (timer) {
diff --git a/app/livechat/imports/server/rest/dashboards.js b/app/livechat/imports/server/rest/dashboards.js
index 0e1f8cedb277d..2537942e74dce 100644
--- a/app/livechat/imports/server/rest/dashboards.js
+++ b/app/livechat/imports/server/rest/dashboards.js
@@ -1,4 +1,4 @@
-import { check } from 'meteor/check';
+import { Match, check } from 'meteor/check';
import { API } from '../../../../api';
import { hasPermission } from '../../../../authorization/server';
@@ -20,8 +20,11 @@ API.v1.addRoute('livechat/analytics/dashboards/conversation-totalizers', { authR
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -33,7 +36,7 @@ API.v1.addRoute('livechat/analytics/dashboards/conversation-totalizers', { authR
}
end = new Date(end);
- const totalizers = getConversationsMetrics({ start, end });
+ const totalizers = getConversationsMetrics({ start, end, departmentId });
return API.v1.success(totalizers);
},
});
@@ -44,8 +47,11 @@ API.v1.addRoute('livechat/analytics/dashboards/agents-productivity-totalizers',
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -57,7 +63,7 @@ API.v1.addRoute('livechat/analytics/dashboards/agents-productivity-totalizers',
}
end = new Date(end);
- const totalizers = getAgentsProductivityMetrics({ start, end });
+ const totalizers = getAgentsProductivityMetrics({ start, end, departmentId });
return API.v1.success(totalizers);
},
});
@@ -68,8 +74,11 @@ API.v1.addRoute('livechat/analytics/dashboards/chats-totalizers', { authRequired
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -81,7 +90,7 @@ API.v1.addRoute('livechat/analytics/dashboards/chats-totalizers', { authRequired
}
end = new Date(end);
- const totalizers = getChatsMetrics({ start, end });
+ const totalizers = getChatsMetrics({ start, end, departmentId });
return API.v1.success(totalizers);
},
});
@@ -92,8 +101,11 @@ API.v1.addRoute('livechat/analytics/dashboards/productivity-totalizers', { authR
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -105,7 +117,7 @@ API.v1.addRoute('livechat/analytics/dashboards/productivity-totalizers', { authR
}
end = new Date(end);
- const totalizers = getProductivityMetrics({ start, end });
+ const totalizers = getProductivityMetrics({ start, end, departmentId });
return API.v1.success(totalizers);
},
@@ -117,8 +129,11 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/chats', { authRequired: tr
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -129,7 +144,7 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/chats', { authRequired: tr
return API.v1.failure('The "end" query parameter must be a valid date.');
}
end = new Date(end);
- const result = findAllChatsStatus({ start, end });
+ const result = findAllChatsStatus({ start, end, departmentId });
return API.v1.success(result);
},
@@ -141,8 +156,11 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/chats-per-agent', { authRe
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -153,7 +171,7 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/chats-per-agent', { authRe
return API.v1.failure('The "end" query parameter must be a valid date.');
}
end = new Date(end);
- const result = findAllChatMetricsByAgent({ start, end });
+ const result = findAllChatMetricsByAgent({ start, end, departmentId });
return API.v1.success(result);
},
@@ -164,7 +182,11 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/agents-status', { authRequ
if (!hasPermission(this.userId, 'view-livechat-manager')) {
return API.v1.unauthorized();
}
- const result = findAllAgentsStatus({});
+
+ const { departmentId } = this.requestParams();
+ check(departmentId, Match.Maybe(String));
+
+ const result = findAllAgentsStatus({ departmentId });
return API.v1.success(result);
},
@@ -176,8 +198,11 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/chats-per-department', { a
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -188,7 +213,7 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/chats-per-department', { a
return API.v1.failure('The "end" query parameter must be a valid date.');
}
end = new Date(end);
- const result = findAllChatMetricsByDepartment({ start, end });
+ const result = findAllChatMetricsByDepartment({ start, end, departmentId });
return API.v1.success(result);
},
@@ -200,8 +225,11 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/timings', { authRequired:
return API.v1.unauthorized();
}
let { start, end } = this.requestParams();
+ const { departmentId } = this.requestParams();
+
check(start, String);
check(end, String);
+ check(departmentId, Match.Maybe(String));
if (isNaN(Date.parse(start))) {
return API.v1.failure('The "start" query parameter must be a valid date.');
@@ -212,7 +240,7 @@ API.v1.addRoute('livechat/analytics/dashboards/charts/timings', { authRequired:
return API.v1.failure('The "end" query parameter must be a valid date.');
}
end = new Date(end);
- const result = findAllResponseTimeMetrics({ start, end });
+ const result = findAllResponseTimeMetrics({ start, end, departmentId });
return API.v1.success(result);
},
diff --git a/app/livechat/server/lib/Analytics.js b/app/livechat/server/lib/Analytics.js
index 6832a8f982d62..b55486751f820 100644
--- a/app/livechat/server/lib/Analytics.js
+++ b/app/livechat/server/lib/Analytics.js
@@ -18,7 +18,9 @@ export const Analytics = {
return;
}
- return this.AgentOverviewData[options.chartOptions.name](from, to);
+ const { departmentId } = options;
+
+ return this.AgentOverviewData[options.chartOptions.name](from, to, departmentId);
},
getAnalyticsChartData(options) {
@@ -43,6 +45,8 @@ export const Analytics = {
dataPoints: [],
};
+ const { departmentId } = options;
+
if (from.diff(to) === 0) { // data for single day
for (let m = moment(from); m.diff(to, 'days') <= 0; m.add(1, 'hours')) {
const hour = m.format('H');
@@ -53,7 +57,7 @@ export const Analytics = {
lt: moment(m).add(1, 'hours'),
};
- data.dataPoints.push(this.ChartData[options.chartOptions.name](date));
+ data.dataPoints.push(this.ChartData[options.chartOptions.name](date, departmentId));
}
} else {
for (let m = moment(from); m.diff(to, 'days') <= 0; m.add(1, 'days')) {
@@ -64,7 +68,7 @@ export const Analytics = {
lt: moment(m).add(1, 'days'),
};
- data.dataPoints.push(this.ChartData[options.chartOptions.name](date));
+ data.dataPoints.push(this.ChartData[options.chartOptions.name](date, departmentId));
}
}
@@ -74,6 +78,7 @@ export const Analytics = {
getAnalyticsOverviewData(options) {
const from = moment(options.daterange.from);
const to = moment(options.daterange.to);
+ const { departmentId } = options;
if (!(moment(from).isValid() && moment(to).isValid())) {
console.log('livechat:getAnalyticsOverviewData => Invalid dates');
@@ -85,7 +90,7 @@ export const Analytics = {
return;
}
- return this.OverviewData[options.analyticsOptions.name](from, to);
+ return this.OverviewData[options.analyticsOptions.name](from, to, departmentId);
},
ChartData: {
@@ -95,15 +100,15 @@ export const Analytics = {
*
* @returns {Integer}
*/
- Total_conversations(date) {
- return LivechatRooms.getTotalConversationsBetweenDate('l', date);
+ Total_conversations(date, departmentId) {
+ return LivechatRooms.getTotalConversationsBetweenDate('l', date, { departmentId });
},
- Avg_chat_duration(date) {
+ Avg_chat_duration(date, departmentId) {
let total = 0;
let count = 0;
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({ metrics }) => {
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
if (metrics && metrics.chatDuration) {
total += metrics.chatDuration;
count++;
@@ -114,10 +119,10 @@ export const Analytics = {
return Math.round(avgCD * 100) / 100;
},
- Total_messages(date) {
+ Total_messages(date, departmentId) {
let total = 0;
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({ msgs }) => {
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ msgs }) => {
if (msgs) {
total += msgs;
}
@@ -132,10 +137,10 @@ export const Analytics = {
*
* @returns {Double}
*/
- Avg_first_response_time(date) {
+ Avg_first_response_time(date, departmentId) {
let frt = 0;
let count = 0;
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({ metrics }) => {
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.response.ft) {
frt += metrics.response.ft;
count++;
@@ -152,10 +157,10 @@ export const Analytics = {
*
* @returns {Double}
*/
- Best_first_response_time(date) {
+ Best_first_response_time(date, departmentId) {
let maxFrt;
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({ metrics }) => {
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.response.ft) {
maxFrt = maxFrt ? Math.min(maxFrt, metrics.response.ft) : metrics.response.ft;
}
@@ -172,10 +177,10 @@ export const Analytics = {
*
* @returns {Double}
*/
- Avg_response_time(date) {
+ Avg_response_time(date, departmentId) {
let art = 0;
let count = 0;
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({ metrics }) => {
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
if (metrics && metrics.response && metrics.response.avg) {
art += metrics.response.avg;
count++;
@@ -193,10 +198,10 @@ export const Analytics = {
*
* @returns {Double}
*/
- Avg_reaction_time(date) {
+ Avg_reaction_time(date, departmentId) {
let arnt = 0;
let count = 0;
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({ metrics }) => {
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({ metrics }) => {
if (metrics && metrics.reaction && metrics.reaction.ft) {
arnt += metrics.reaction.ft;
count++;
@@ -237,7 +242,7 @@ export const Analytics = {
*
* @returns {Array[Object]}
*/
- Conversations(from, to) {
+ Conversations(from, to, departmentId) {
let totalConversations = 0; // Total conversations
let openConversations = 0; // open conversations
let totalMessages = 0; // total msgs
@@ -261,7 +266,7 @@ export const Analytics = {
lt: moment(m).add(1, 'days'),
};
- const result = Promise.await(LivechatRooms.getAnalyticsBetweenDate(date).toArray());
+ const result = Promise.await(LivechatRooms.getAnalyticsBetweenDate(date, { departmentId }).toArray());
totalConversations += result.length;
result.forEach(summarize(m));
@@ -278,7 +283,7 @@ export const Analytics = {
gte: h,
lt: moment(h).add(1, 'hours'),
};
- Promise.await(LivechatRooms.getAnalyticsBetweenDate(date).toArray()).forEach(({
+ Promise.await(LivechatRooms.getAnalyticsBetweenDate(date, { departmentId }).toArray()).forEach(({
msgs,
}) => {
const dayHour = h.format('H'); // @int : 0, 1, ... 23
@@ -319,7 +324,7 @@ export const Analytics = {
*
* @returns {Array[Object]}
*/
- Productivity(from, to) {
+ Productivity(from, to, departmentId) {
let avgResponseTime = 0;
let firstResponseTime = 0;
let avgReactionTime = 0;
@@ -330,7 +335,7 @@ export const Analytics = {
lt: to.add(1, 'days'),
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
metrics,
}) => {
if (metrics && metrics.response && metrics.reaction) {
@@ -395,7 +400,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Total_conversations(from, to) {
+ Total_conversations(from, to, departmentId) {
let total = 0;
const agentConversations = new Map(); // stores total conversations for each agent
const date = {
@@ -412,7 +417,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
servedBy,
}) => {
if (servedBy) {
@@ -446,7 +451,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Avg_chat_duration(from, to) {
+ Avg_chat_duration(from, to, departmentId) {
const agentChatDurations = new Map(); // stores total conversations for each agent
const date = {
gte: from,
@@ -462,7 +467,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
metrics,
servedBy,
}) => {
@@ -506,7 +511,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Total_messages(from, to) {
+ Total_messages(from, to, departmentId) {
const agentMessages = new Map(); // stores total conversations for each agent
const date = {
gte: from,
@@ -522,7 +527,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
servedBy,
msgs,
}) => {
@@ -550,7 +555,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Avg_first_response_time(from, to) {
+ Avg_first_response_time(from, to, departmentId) {
const agentAvgRespTime = new Map(); // stores avg response time for each agent
const date = {
gte: from,
@@ -566,7 +571,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
metrics,
servedBy,
}) => {
@@ -610,7 +615,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Best_first_response_time(from, to) {
+ Best_first_response_time(from, to, departmentId) {
const agentFirstRespTime = new Map(); // stores avg response time for each agent
const date = {
gte: from,
@@ -626,7 +631,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
metrics,
servedBy,
}) => {
@@ -662,7 +667,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Avg_response_time(from, to) {
+ Avg_response_time(from, to, departmentId) {
const agentAvgRespTime = new Map(); // stores avg response time for each agent
const date = {
gte: from,
@@ -678,7 +683,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
metrics,
servedBy,
}) => {
@@ -722,7 +727,7 @@ export const Analytics = {
*
* @returns {Array(Object), Array(Object)}
*/
- Avg_reaction_time(from, to) {
+ Avg_reaction_time(from, to, departmentId) {
const agentAvgReactionTime = new Map(); // stores avg reaction time for each agent
const date = {
gte: from,
@@ -738,7 +743,7 @@ export const Analytics = {
data: [],
};
- LivechatRooms.getAnalyticsMetricsBetweenDate('l', date).forEach(({
+ LivechatRooms.getAnalyticsMetricsBetweenDate('l', date, { departmentId }).forEach(({
metrics,
servedBy,
}) => {
diff --git a/app/livechat/server/lib/analytics/dashboards.js b/app/livechat/server/lib/analytics/dashboards.js
index 62fa53e8ee42a..e3d9c2db552c6 100644
--- a/app/livechat/server/lib/analytics/dashboards.js
+++ b/app/livechat/server/lib/analytics/dashboards.js
@@ -43,6 +43,7 @@ const getProductivityMetricsAsync = async ({
analyticsOptions: {
name: 'Productivity',
},
+ departmentId,
});
const averageWaitingTime = await findAllAverageWaitingTimeAsync({
start,
@@ -91,6 +92,7 @@ const getAgentsProductivityMetricsAsync = async ({
analyticsOptions: {
name: 'Conversations',
},
+ departmentId,
});
const totalOfServiceTime = averageOfServiceTime.departments.length;
@@ -166,6 +168,7 @@ const getChatsMetricsAsync = async ({
const getConversationsMetricsAsync = async ({
start,
end,
+ departmentId,
}) => {
if (!start || !end) {
throw new Error('"start" and "end" must be provided');
@@ -178,9 +181,10 @@ const getConversationsMetricsAsync = async ({
analyticsOptions: {
name: 'Conversations',
},
+ ...departmentId && departmentId !== 'undefined' && { departmentId },
});
const metrics = ['Total_conversations', 'Open_conversations', 'Total_messages'];
- const visitorsCount = await LivechatVisitors.getVisitorsBetweenDate({ start, end }).count();
+ const visitorsCount = await LivechatVisitors.getVisitorsBetweenDate({ start, end, department: departmentId }).count();
return {
totalizers: [
...totalizers.filter((metric) => metrics.includes(metric.title)),
diff --git a/app/models/server/models/LivechatRooms.js b/app/models/server/models/LivechatRooms.js
index 7e9ceb876ce2b..25e2e51c6be2b 100644
--- a/app/models/server/models/LivechatRooms.js
+++ b/app/models/server/models/LivechatRooms.js
@@ -351,31 +351,33 @@ export class LivechatRooms extends Base {
}, update);
}
- getTotalConversationsBetweenDate(t, date) {
+ getTotalConversationsBetweenDate(t, date, { departmentId } = {}) {
const query = {
t,
ts: {
$gte: new Date(date.gte), // ISO Date, ts >= date.gte
$lt: new Date(date.lt), // ISODate, ts < date.lt
},
+ ...departmentId && departmentId !== 'undefined' && { departmentId },
};
return this.find(query).count();
}
- getAnalyticsMetricsBetweenDate(t, date) {
+ getAnalyticsMetricsBetweenDate(t, date, { departmentId } = {}) {
const query = {
t,
ts: {
$gte: new Date(date.gte), // ISO Date, ts >= date.gte
$lt: new Date(date.lt), // ISODate, ts < date.lt
},
+ ...departmentId && departmentId !== 'undefined' && { departmentId },
};
return this.find(query, { fields: { ts: 1, departmentId: 1, open: 1, servedBy: 1, metrics: 1, msgs: 1 } });
}
- getAnalyticsBetweenDate(date) {
+ getAnalyticsBetweenDate(date, { departmentId } = {}) {
return this.model.rawCollection().aggregate([
{
$match: {
@@ -384,6 +386,7 @@ export class LivechatRooms extends Base {
$gte: new Date(date.gte), // ISO Date, ts >= date.gte
$lt: new Date(date.lt), // ISODate, ts < date.lt
},
+ ...departmentId && departmentId !== 'undefined' && { departmentId },
},
},
{
diff --git a/app/models/server/raw/LivechatAgentActivity.js b/app/models/server/raw/LivechatAgentActivity.js
index 7a86cf1bac8af..9d48cf45a8328 100644
--- a/app/models/server/raw/LivechatAgentActivity.js
+++ b/app/models/server/raw/LivechatAgentActivity.js
@@ -57,7 +57,7 @@ export class LivechatAgentActivityRaw extends BaseRaw {
},
};
const params = [match];
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
params.push(lookup);
params.push(unwind);
params.push(departmentsMatch);
diff --git a/app/models/server/raw/LivechatRooms.js b/app/models/server/raw/LivechatRooms.js
index e996eac6e3681..39fa94546222b 100644
--- a/app/models/server/raw/LivechatRooms.js
+++ b/app/models/server/raw/LivechatRooms.js
@@ -5,7 +5,7 @@ export class LivechatRoomsRaw extends BaseRaw {
getQueueMetrics({ departmentId, agentId, includeOfflineAgents, options = {} }) {
const match = { $match: { t: 'l', open: true, servedBy: { $exists: true } } };
const matchUsers = { $match: {} };
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
if (agentId) {
@@ -118,7 +118,7 @@ export class LivechatRoomsRaw extends BaseRaw {
abandonedRooms: 1,
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -176,7 +176,7 @@ export class LivechatRoomsRaw extends BaseRaw {
},
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -218,7 +218,7 @@ export class LivechatRoomsRaw extends BaseRaw {
averageChatDurationTimeInSeconds: { $ceil: { $cond: [{ $eq: ['$rooms', 0] }, 0, { $divide: ['$chatsDuration', '$rooms'] }] } },
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -260,7 +260,7 @@ export class LivechatRoomsRaw extends BaseRaw {
averageWaitingTimeInSeconds: { $ceil: { $cond: [{ $eq: ['$rooms', 0] }, 0, { $divide: ['$chatsFirstResponses', '$rooms'] }] } },
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -303,7 +303,7 @@ export class LivechatRoomsRaw extends BaseRaw {
rooms: 1,
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -347,7 +347,7 @@ export class LivechatRoomsRaw extends BaseRaw {
serviceTimeDuration: { $ceil: '$serviceTimeDuration' },
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -455,7 +455,7 @@ export class LivechatRoomsRaw extends BaseRaw {
},
};
const firstParams = [match, departmentsLookup, departmentsUnwind];
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
firstParams.push({
$match: {
'departments._id': departmentId,
@@ -482,7 +482,7 @@ export class LivechatRoomsRaw extends BaseRaw {
servedBy: { $exists: true },
ts: { $gte: new Date(start), $lte: new Date(end) },
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
query.departmentId = departmentId;
}
return this.find(query).count();
@@ -497,7 +497,7 @@ export class LivechatRoomsRaw extends BaseRaw {
servedBy: { $exists: true },
ts: { $gte: new Date(start), $lte: new Date(end) },
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
query.departmentId = departmentId;
}
return this.find(query).count();
@@ -509,7 +509,7 @@ export class LivechatRoomsRaw extends BaseRaw {
servedBy: { $exists: false },
ts: { $gte: new Date(start), $lte: new Date(end) },
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
query.departmentId = departmentId;
}
return this.find(query).count();
@@ -530,7 +530,7 @@ export class LivechatRoomsRaw extends BaseRaw {
chats: { $sum: 1 },
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
return this.col.aggregate([match, group]).toArray();
@@ -552,7 +552,7 @@ export class LivechatRoomsRaw extends BaseRaw {
chats: { $sum: 1 },
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
return this.col.aggregate([match, group]).toArray();
@@ -597,7 +597,7 @@ export class LivechatRoomsRaw extends BaseRaw {
chats: 1,
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const params = [match, lookup, unwind, group, project];
@@ -643,7 +643,7 @@ export class LivechatRoomsRaw extends BaseRaw {
chats: 1,
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const params = [match, lookup, unwind, group, project];
@@ -689,7 +689,7 @@ export class LivechatRoomsRaw extends BaseRaw {
longest: '$maxFirstResponse',
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
return this.col.aggregate([match, group, project]).toArray();
@@ -734,7 +734,7 @@ export class LivechatRoomsRaw extends BaseRaw {
longest: '$maxFirstReaction',
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
return this.col.aggregate([match, group, project]).toArray();
@@ -780,7 +780,7 @@ export class LivechatRoomsRaw extends BaseRaw {
longest: '$maxChatDuration',
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
return this.col.aggregate([match, group, project]).toArray();
@@ -811,7 +811,7 @@ export class LivechatRoomsRaw extends BaseRaw {
averageServiceTimeInSeconds: { $ceil: { $cond: [{ $eq: ['$rooms', 0] }, 0, { $divide: ['$allServiceTime', '$rooms'] }] } },
},
};
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
match.$match.departmentId = departmentId;
}
const sort = { $sort: options.sort || { name: 1 } };
@@ -848,7 +848,7 @@ export class LivechatRoomsRaw extends BaseRaw {
if (roomName) {
query.fname = new RegExp(roomName, 'i');
}
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
query.departmentId = departmentId;
}
if (open !== undefined) {
diff --git a/app/models/server/raw/LivechatVisitors.js b/app/models/server/raw/LivechatVisitors.js
index 4faadab58a7d9..7ee3f02f2cca5 100644
--- a/app/models/server/raw/LivechatVisitors.js
+++ b/app/models/server/raw/LivechatVisitors.js
@@ -1,12 +1,13 @@
import { BaseRaw } from './BaseRaw';
export class LivechatVisitorsRaw extends BaseRaw {
- getVisitorsBetweenDate({ start, end }) {
+ getVisitorsBetweenDate({ start, end, department }) {
const query = {
_updatedAt: {
$gte: new Date(start),
$lt: new Date(end),
},
+ ...department && department !== 'undefined' && { department },
};
return this.find(query, { fields: { _id: 1 } });
diff --git a/app/models/server/raw/Users.js b/app/models/server/raw/Users.js
index 803e1c82e1282..245ba2b579df1 100644
--- a/app/models/server/raw/Users.js
+++ b/app/models/server/raw/Users.js
@@ -215,7 +215,7 @@ export class UsersRaw extends BaseRaw {
},
};
const params = [match];
- if (departmentId) {
+ if (departmentId && departmentId !== 'undefined') {
params.push(lookup);
params.push(unwind);
params.push(departmentsMatch);