feat: Add report on customer waiting time (#7545)

This commit is contained in:
Pranav Raj S
2023-07-20 12:01:22 -07:00
committed by GitHub
parent d7566c453d
commit 27419eef66
9 changed files with 81 additions and 82 deletions

View File

@@ -20,7 +20,7 @@ class V2::ReportBuilder
# For backward compatible with old report # For backward compatible with old report
def build def build
if %w[avg_first_response_time avg_resolution_time].include?(params[:metric]) if %w[avg_first_response_time avg_resolution_time reply_time].include?(params[:metric])
timeseries.each_with_object([]) do |p, arr| timeseries.each_with_object([]) do |p, arr|
arr << { value: p[1], timestamp: p[0].in_time_zone(@timezone).to_i, count: @grouped_values.count[p[0]] } arr << { value: p[1], timestamp: p[0].in_time_zone(@timezone).to_i, count: @grouped_values.count[p[0]] }
end end
@@ -38,7 +38,8 @@ class V2::ReportBuilder
outgoing_messages_count: outgoing_messages.count, outgoing_messages_count: outgoing_messages.count,
avg_first_response_time: avg_first_response_time_summary, avg_first_response_time: avg_first_response_time_summary,
avg_resolution_time: avg_resolution_time_summary, avg_resolution_time: avg_resolution_time_summary,
resolutions_count: resolutions.count resolutions_count: resolutions.count,
reply_time: reply_time_summary
} }
end end

View File

@@ -56,6 +56,13 @@ module ReportHelper
grouped_reporting_events.average(:value) grouped_reporting_events.average(:value)
end end
def reply_time
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'reply_time', account_id: account.id))
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
grouped_reporting_events.average(:value)
end
def avg_resolution_time def avg_resolution_time
grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'conversation_resolved', account_id: account.id)) grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'conversation_resolved', account_id: account.id))
return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours] return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours]
@@ -77,6 +84,16 @@ module ReportHelper
avg_rt avg_rt
end end
def reply_time_summary
reporting_events = scope.reporting_events
.where(name: 'reply_time', account_id: account.id, created_at: range)
reply_time = params[:business_hours] ? reporting_events.average(:value_in_business_hours) : reporting_events.average(:value)
return 0 if reply_time.blank?
reply_time
end
def avg_first_response_time_summary def avg_first_response_time_summary
reporting_events = scope.reporting_events reporting_events = scope.reporting_events
.where(name: 'first_response', account_id: account.id, created_at: range) .where(name: 'first_response', account_id: account.id, created_at: range)

View File

@@ -34,6 +34,10 @@
"RESOLUTION_COUNT": { "RESOLUTION_COUNT": {
"NAME": "Resolution Count", "NAME": "Resolution Count",
"DESC": "( Total )" "DESC": "( Total )"
},
"REPLY_TIME": {
"NAME": "Customer waiting time",
"TOOLTIP_TEXT": "Waiting time is %{metricValue} (based on %{conversationCount} conversations)"
} }
}, },
"DATE_RANGE_OPTIONS": { "DATE_RANGE_OPTIONS": {

View File

@@ -7,19 +7,13 @@ export default {
accountSummary: 'getAccountSummary', accountSummary: 'getAccountSummary',
accountReport: 'getAccountReports', accountReport: 'getAccountReports',
}), }),
calculateTrend() {
return metric_key => {
if (!this.accountSummary.previous[metric_key]) return 0;
const diff =
this.accountSummary[metric_key] -
this.accountSummary.previous[metric_key];
return Math.round(
(diff / this.accountSummary.previous[metric_key]) * 100
);
};
},
}, },
methods: { methods: {
calculateTrend(key) {
if (!this.accountSummary.previous[key]) return 0;
const diff = this.accountSummary[key] - this.accountSummary.previous[key];
return Math.round((diff / this.accountSummary.previous[key]) * 100);
},
displayMetric(key) { displayMetric(key) {
if (this.isAverageMetricType(key)) { if (this.isAverageMetricType(key)) {
return formatTime(this.accountSummary[key]); return formatTime(this.accountSummary[key]);
@@ -39,7 +33,11 @@ export default {
return ''; return '';
}, },
isAverageMetricType(key) { isAverageMetricType(key) {
return ['avg_first_response_time', 'avg_resolution_time'].includes(key); return [
'avg_first_response_time',
'avg_resolution_time',
'reply_time',
].includes(key);
}, },
}, },
}; };

View File

@@ -35,6 +35,7 @@ const REPORTS_KEYS = {
FIRST_RESPONSE_TIME: 'avg_first_response_time', FIRST_RESPONSE_TIME: 'avg_first_response_time',
RESOLUTION_TIME: 'avg_resolution_time', RESOLUTION_TIME: 'avg_resolution_time',
RESOLUTION_COUNT: 'resolutions_count', RESOLUTION_COUNT: 'resolutions_count',
REPLY_TIME: 'reply_time',
}; };
export default { export default {
@@ -78,6 +79,7 @@ export default {
'FIRST_RESPONSE_TIME', 'FIRST_RESPONSE_TIME',
'RESOLUTION_TIME', 'RESOLUTION_TIME',
'RESOLUTION_COUNT', 'RESOLUTION_COUNT',
'REPLY_TIME',
].forEach(async key => { ].forEach(async key => {
try { try {
await this.$store.dispatch('fetchAccountReport', { await this.$store.dispatch('fetchAccountReport', {

View File

@@ -44,6 +44,7 @@ const REPORTS_KEYS = {
FIRST_RESPONSE_TIME: 'avg_first_response_time', FIRST_RESPONSE_TIME: 'avg_first_response_time',
RESOLUTION_TIME: 'avg_resolution_time', RESOLUTION_TIME: 'avg_resolution_time',
RESOLUTION_COUNT: 'resolutions_count', RESOLUTION_COUNT: 'resolutions_count',
REPLY_TIME: 'reply_time',
}; };
export default { export default {
@@ -60,6 +61,7 @@ export default {
const reportKeys = [ const reportKeys = [
'CONVERSATIONS', 'CONVERSATIONS',
'FIRST_RESPONSE_TIME', 'FIRST_RESPONSE_TIME',
'REPLY_TIME',
'RESOLUTION_TIME', 'RESOLUTION_TIME',
'RESOLUTION_COUNT', 'RESOLUTION_COUNT',
'INCOMING_MESSAGES', 'INCOMING_MESSAGES',

View File

@@ -38,6 +38,7 @@ const REPORTS_KEYS = {
FIRST_RESPONSE_TIME: 'avg_first_response_time', FIRST_RESPONSE_TIME: 'avg_first_response_time',
RESOLUTION_TIME: 'avg_resolution_time', RESOLUTION_TIME: 'avg_resolution_time',
RESOLUTION_COUNT: 'resolutions_count', RESOLUTION_COUNT: 'resolutions_count',
REPLY_TIME: 'reply_time',
}; };
export default { export default {
@@ -106,6 +107,7 @@ export default {
'FIRST_RESPONSE_TIME', 'FIRST_RESPONSE_TIME',
'RESOLUTION_TIME', 'RESOLUTION_TIME',
'RESOLUTION_COUNT', 'RESOLUTION_COUNT',
'REPLY_TIME',
].forEach(async key => { ].forEach(async key => {
try { try {
const { from, to, groupBy, businessHours } = this; const { from, to, groupBy, businessHours } = this;

View File

@@ -151,78 +151,48 @@ export const DEFAULT_CHART = {
}, },
}; };
const TIME_CHART_CONFIG = {
datasets: [DEFAULT_BAR_CHART],
scales: {
xAxes: [
{
ticks: {
fontFamily: CHART_FONT_FAMILY,
},
gridLines: {
drawOnChartArea: false,
},
},
],
yAxes: [
{
id: 'y-left',
type: 'linear',
position: 'left',
ticks: {
fontFamily: CHART_FONT_FAMILY,
callback: (value, index, values) => {
if (!index || index === values.length - 1) {
return formatTime(value);
}
return '';
},
},
gridLines: {
drawOnChartArea: false,
},
},
],
},
};
export const METRIC_CHART = { export const METRIC_CHART = {
conversations_count: DEFAULT_CHART, conversations_count: DEFAULT_CHART,
incoming_messages_count: DEFAULT_CHART, incoming_messages_count: DEFAULT_CHART,
outgoing_messages_count: DEFAULT_CHART, outgoing_messages_count: DEFAULT_CHART,
avg_first_response_time: { avg_first_response_time: TIME_CHART_CONFIG,
datasets: [DEFAULT_BAR_CHART], reply_time: TIME_CHART_CONFIG,
scales: { avg_resolution_time: TIME_CHART_CONFIG,
xAxes: [
{
ticks: {
fontFamily: CHART_FONT_FAMILY,
},
gridLines: {
drawOnChartArea: false,
},
},
],
yAxes: [
{
id: 'y-left',
type: 'linear',
position: 'left',
ticks: {
fontFamily: CHART_FONT_FAMILY,
callback: (value, index, values) => {
if (!index || index === values.length - 1) {
return formatTime(value);
}
return '';
},
},
gridLines: {
drawOnChartArea: false,
},
},
],
},
},
avg_resolution_time: {
datasets: [DEFAULT_BAR_CHART],
scales: {
xAxes: [
{
ticks: {
fontFamily: CHART_FONT_FAMILY,
},
gridLines: {
drawOnChartArea: false,
},
},
],
yAxes: [
{
id: 'y-left',
type: 'linear',
position: 'left',
ticks: {
fontFamily: CHART_FONT_FAMILY,
callback: (value, index, values) => {
if (!index || index === values.length - 1) {
return formatTime(value);
}
return '';
},
},
gridLines: {
drawOnChartArea: false,
},
},
],
},
},
resolutions_count: DEFAULT_CHART, resolutions_count: DEFAULT_CHART,
}; };

View File

@@ -19,6 +19,7 @@ const state = {
avg_first_response_time: false, avg_first_response_time: false,
avg_resolution_time: false, avg_resolution_time: false,
resolutions_count: false, resolutions_count: false,
reply_time: false,
}, },
data: { data: {
conversations_count: [], conversations_count: [],
@@ -27,6 +28,7 @@ const state = {
avg_first_response_time: [], avg_first_response_time: [],
avg_resolution_time: [], avg_resolution_time: [],
resolutions_count: [], resolutions_count: [],
reply_time: [],
}, },
}, },
accountSummary: { accountSummary: {
@@ -35,6 +37,7 @@ const state = {
conversations_count: 0, conversations_count: 0,
incoming_messages_count: 0, incoming_messages_count: 0,
outgoing_messages_count: 0, outgoing_messages_count: 0,
reply_time: 0,
resolutions_count: 0, resolutions_count: 0,
previous: {}, previous: {},
}, },