feat: Add report on customer waiting time (#7545)
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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', {
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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: {},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user