feat: Add business hours in downloadable reports (#4674)
Co-authored-by: Aswin Dev P.S <aswindevps@gmail.com>
This commit is contained in:
parent
dceeb57a1d
commit
52fad886b8
13 changed files with 138 additions and 67 deletions
|
@ -1,4 +1,5 @@
|
|||
class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
||||
include Api::V2::Accounts::ReportsHelper
|
||||
before_action :check_authorization
|
||||
|
||||
def index
|
||||
|
@ -12,27 +13,23 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||
end
|
||||
|
||||
def agents
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=agents_report.csv'
|
||||
render layout: false, template: 'api/v2/accounts/reports/agents.csv.erb', format: 'csv'
|
||||
@report_data = generate_agents_report
|
||||
generate_csv('agents_report', 'api/v2/accounts/reports/agents.csv.erb')
|
||||
end
|
||||
|
||||
def inboxes
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=inboxes_report.csv'
|
||||
render layout: false, template: 'api/v2/accounts/reports/inboxes.csv.erb', format: 'csv'
|
||||
@report_data = generate_inboxes_report
|
||||
generate_csv('inboxes_report', 'api/v2/accounts/reports/inboxes.csv.erb')
|
||||
end
|
||||
|
||||
def labels
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=labels_report.csv'
|
||||
render layout: false, template: 'api/v2/accounts/reports/labels.csv.erb', format: 'csv'
|
||||
@report_data = generate_labels_report
|
||||
generate_csv('labels_report', 'api/v2/accounts/reports/labels.csv.erb')
|
||||
end
|
||||
|
||||
def teams
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=teams_report.csv'
|
||||
render layout: false, template: 'api/v2/accounts/reports/teams.csv.erb', format: 'csv'
|
||||
@report_data = generate_teams_report
|
||||
generate_csv('teams_report', 'api/v2/accounts/reports/teams.csv.erb')
|
||||
end
|
||||
|
||||
def conversations
|
||||
|
@ -43,6 +40,12 @@ class Api::V2::Accounts::ReportsController < Api::V1::Accounts::BaseController
|
|||
|
||||
private
|
||||
|
||||
def generate_csv(filename, template)
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = "attachment; filename=#{filename}.csv"
|
||||
render layout: false, template: template, format: 'csv'
|
||||
end
|
||||
|
||||
def check_authorization
|
||||
raise Pundit::NotAuthorizedError unless Current.account_user.administrator?
|
||||
end
|
||||
|
|
56
app/helpers/api/v2/accounts/reports_helper.rb
Normal file
56
app/helpers/api/v2/accounts/reports_helper.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
module Api::V2::Accounts::ReportsHelper
|
||||
def generate_agents_report
|
||||
Current.account.users.map do |agent|
|
||||
agent_report = generate_report({ type: :agent, id: agent.id })
|
||||
[agent.name] + generate_readable_report_metrics(agent_report)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_inboxes_report
|
||||
Current.account.inboxes.map do |inbox|
|
||||
inbox_report = generate_report({ type: :inbox, id: inbox.id })
|
||||
[inbox.name, inbox.channel&.name] + generate_readable_report_metrics(inbox_report)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_teams_report
|
||||
Current.account.teams.map do |team|
|
||||
team_report = generate_report({ type: :team, id: team.id })
|
||||
[team.name] + generate_readable_report_metrics(team_report)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_labels_report
|
||||
Current.account.labels.map do |label|
|
||||
label_report = generate_report({ type: :label, id: label.id })
|
||||
[label.title] + generate_readable_report_metrics(label_report)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_report(report_params)
|
||||
V2::ReportBuilder.new(
|
||||
Current.account,
|
||||
report_params.merge(
|
||||
{
|
||||
since: params[:since],
|
||||
until: params[:until],
|
||||
business_hours: ActiveModel::Type::Boolean.new.cast(params[:business_hours])
|
||||
}
|
||||
)
|
||||
).summary
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_readable_report_metrics(report_metric)
|
||||
[
|
||||
report_metric[:conversations_count],
|
||||
time_to_minutes(report_metric[:avg_first_response_time]),
|
||||
time_to_minutes(report_metric[:avg_resolution_time])
|
||||
]
|
||||
end
|
||||
|
||||
def time_to_minutes(time_in_seconds)
|
||||
(time_in_seconds / 60).to_i
|
||||
end
|
||||
end
|
|
@ -53,27 +53,27 @@ class ReportsAPI extends ApiClient {
|
|||
});
|
||||
}
|
||||
|
||||
getAgentReports(since, until) {
|
||||
getAgentReports({ from: since, to: until, businessHours }) {
|
||||
return axios.get(`${this.url}/agents`, {
|
||||
params: { since, until },
|
||||
params: { since, until, business_hours: businessHours },
|
||||
});
|
||||
}
|
||||
|
||||
getLabelReports(since, until) {
|
||||
getLabelReports({ from: since, to: until, businessHours }) {
|
||||
return axios.get(`${this.url}/labels`, {
|
||||
params: { since, until },
|
||||
params: { since, until, business_hours: businessHours },
|
||||
});
|
||||
}
|
||||
|
||||
getInboxReports(since, until) {
|
||||
getInboxReports({ from: since, to: until, businessHours }) {
|
||||
return axios.get(`${this.url}/inboxes`, {
|
||||
params: { since, until },
|
||||
params: { since, until, business_hours: businessHours },
|
||||
});
|
||||
}
|
||||
|
||||
getTeamReports(since, until) {
|
||||
getTeamReports({ from: since, to: until, businessHours }) {
|
||||
return axios.get(`${this.url}/teams`, {
|
||||
params: { since, until },
|
||||
params: { since, until, business_hours: businessHours },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,20 +47,25 @@ describe('#Reports API', () => {
|
|||
});
|
||||
|
||||
it('#getAgentReports', () => {
|
||||
reportsAPI.getAgentReports(1621103400, 1621621800);
|
||||
reportsAPI.getAgentReports({
|
||||
from: 1621103400,
|
||||
to: 1621621800,
|
||||
businessHours: true,
|
||||
});
|
||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||
'/api/v2/reports/agents',
|
||||
{
|
||||
params: {
|
||||
since: 1621103400,
|
||||
until: 1621621800,
|
||||
business_hours: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('#getLabelReports', () => {
|
||||
reportsAPI.getLabelReports(1621103400, 1621621800);
|
||||
reportsAPI.getLabelReports({ from: 1621103400, to: 1621621800 });
|
||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||
'/api/v2/reports/labels',
|
||||
{
|
||||
|
@ -73,7 +78,7 @@ describe('#Reports API', () => {
|
|||
});
|
||||
|
||||
it('#getInboxReports', () => {
|
||||
reportsAPI.getInboxReports(1621103400, 1621621800);
|
||||
reportsAPI.getInboxReports({ from: 1621103400, to: 1621621800 });
|
||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||
'/api/v2/reports/inboxes',
|
||||
{
|
||||
|
@ -86,7 +91,7 @@ describe('#Reports API', () => {
|
|||
});
|
||||
|
||||
it('#getTeamReports', () => {
|
||||
reportsAPI.getTeamReports(1621103400, 1621621800);
|
||||
reportsAPI.getTeamReports({ from: 1621103400, to: 1621621800 });
|
||||
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||
'/api/v2/reports/teams',
|
||||
{
|
||||
|
|
|
@ -13,5 +13,10 @@ export const downloadCsvFile = (fileName, content) => {
|
|||
return link;
|
||||
};
|
||||
|
||||
export const generateFileName = ({ type, to }) =>
|
||||
`${type}-report-${format(fromUnixTime(to), 'dd-MM-yyyy')}.csv`;
|
||||
export const generateFileName = ({ type, to, businessHours = false }) => {
|
||||
let name = `${type}-report-${format(fromUnixTime(to), 'dd-MM-yyyy')}`;
|
||||
if (businessHours) {
|
||||
name = `${name}-business-hours`;
|
||||
}
|
||||
return `${name}.csv`;
|
||||
};
|
||||
|
|
|
@ -5,5 +5,9 @@ describe('#generateFileName', () => {
|
|||
expect(generateFileName({ type: 'csat', to: 1652812199 })).toEqual(
|
||||
'csat-report-17-05-2022.csv'
|
||||
);
|
||||
|
||||
expect(
|
||||
generateFileName({ type: 'csat', to: 1652812199, businessHours: true })
|
||||
).toEqual('csat-report-17-05-2022-business-hours.csv');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -251,7 +251,7 @@ export default {
|
|||
});
|
||||
},
|
||||
downloadReports() {
|
||||
const { from, to, type } = this;
|
||||
const { from, to, type, businessHours } = this;
|
||||
const dispatchMethods = {
|
||||
agent: 'downloadAgentReports',
|
||||
label: 'downloadLabelReports',
|
||||
|
@ -259,8 +259,8 @@ export default {
|
|||
team: 'downloadTeamReports',
|
||||
};
|
||||
if (dispatchMethods[type]) {
|
||||
const fileName = generateFileName({ type, to });
|
||||
const params = { from, to, fileName };
|
||||
const fileName = generateFileName({ type, to, businessHours });
|
||||
const params = { from, to, fileName, businessHours };
|
||||
this.$store.dispatch(dispatchMethods[type], params);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -120,7 +120,7 @@ export const actions = {
|
|||
commit(types.default.UPDATE_REPORT_AGENTS_STATUS, data);
|
||||
},
|
||||
downloadAgentReports(_, reportObj) {
|
||||
return Report.getAgentReports(reportObj.from, reportObj.to)
|
||||
return Report.getAgentReports(reportObj)
|
||||
.then(response => {
|
||||
downloadCsvFile(reportObj.fileName, response.data);
|
||||
})
|
||||
|
@ -129,7 +129,7 @@ export const actions = {
|
|||
});
|
||||
},
|
||||
downloadLabelReports(_, reportObj) {
|
||||
return Report.getLabelReports(reportObj.from, reportObj.to)
|
||||
return Report.getLabelReports(reportObj)
|
||||
.then(response => {
|
||||
downloadCsvFile(reportObj.fileName, response.data);
|
||||
})
|
||||
|
@ -138,7 +138,7 @@ export const actions = {
|
|||
});
|
||||
},
|
||||
downloadInboxReports(_, reportObj) {
|
||||
return Report.getInboxReports(reportObj.from, reportObj.to)
|
||||
return Report.getInboxReports(reportObj)
|
||||
.then(response => {
|
||||
downloadCsvFile(reportObj.fileName, response.data);
|
||||
})
|
||||
|
@ -147,7 +147,7 @@ export const actions = {
|
|||
});
|
||||
},
|
||||
downloadTeamReports(_, reportObj) {
|
||||
return Report.getTeamReports(reportObj.from, reportObj.to)
|
||||
return Report.getTeamReports(reportObj)
|
||||
.then(response => {
|
||||
downloadCsvFile(reportObj.fileName, response.data);
|
||||
})
|
||||
|
|
|
@ -6,14 +6,7 @@
|
|||
]
|
||||
%>
|
||||
<%= CSV.generate_line headers -%>
|
||||
<% Current.account.users.each do |agent| %>
|
||||
<% agent_report = V2::ReportBuilder.new(Current.account, {
|
||||
type: :agent,
|
||||
id: agent.id,
|
||||
since: params[:since],
|
||||
until: params[:until]
|
||||
}).summary %>
|
||||
<% row = [ agent.name, agent_report[:conversations_count], (agent_report[:avg_first_response_time]/60).to_i, (agent_report[:avg_resolution_time]/60).to_i ] %>
|
||||
<% @report_data.each do |row| %>
|
||||
<%= CSV.generate_line row -%>
|
||||
<% end %>
|
||||
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<% headers = ['Inbox name', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %>
|
||||
<% headers = [
|
||||
I18n.t('reports.inbox_csv.inbox_name'),
|
||||
I18n.t('reports.inbox_csv.inbox_type'),
|
||||
I18n.t('reports.inbox_csv.conversations_count'),
|
||||
I18n.t('reports.inbox_csv.avg_first_response_time'),
|
||||
I18n.t('reports.inbox_csv.avg_resolution_time')
|
||||
]
|
||||
%>
|
||||
<%= CSV.generate_line headers -%>
|
||||
<% Current.account.inboxes.each do |inbox| %>
|
||||
<% inbox_report = V2::ReportBuilder.new(Current.account, {
|
||||
type: :inbox,
|
||||
id: inbox.id,
|
||||
since: params[:since],
|
||||
until: params[:until]
|
||||
}).summary %>
|
||||
<% row = [ inbox.name, inbox_report[:conversations_count], (inbox_report[:avg_first_response_time]/60).to_i, (inbox_report[:avg_resolution_time]/60).to_i ] %>
|
||||
<% @report_data.each do |row| %>
|
||||
<%= CSV.generate_line row -%>
|
||||
<% end %>
|
||||
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<% headers = ['Label Title', 'Conversations count', 'Avg first response time (Minutes)', 'Avg resolution time (Minutes)'] %>
|
||||
<% headers = [
|
||||
I18n.t('reports.label_csv.label_title'),
|
||||
I18n.t('reports.label_csv.conversations_count'),
|
||||
I18n.t('reports.label_csv.avg_first_response_time'),
|
||||
I18n.t('reports.label_csv.avg_resolution_time')
|
||||
]
|
||||
%>
|
||||
<%= CSV.generate_line headers -%>
|
||||
<% Current.account.labels.each do |label| %>
|
||||
<% label_report = V2::ReportBuilder.new(Current.account, {
|
||||
type: :label,
|
||||
id: label.id,
|
||||
since: params[:since],
|
||||
until: params[:until]
|
||||
}).summary %>
|
||||
<% row = [ label.title, label_report[:conversations_count], (label_report[:avg_first_response_time]/60).to_i, (label_report[:avg_resolution_time]/60).to_i ] %>
|
||||
<% @report_data.each do |row| %>
|
||||
<%= CSV.generate_line row -%>
|
||||
<% end %>
|
||||
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||
|
|
|
@ -6,14 +6,7 @@
|
|||
]
|
||||
%>
|
||||
<%= CSV.generate_line headers -%>
|
||||
<% Current.account.teams.each do |team| %>
|
||||
<% team_report = V2::ReportBuilder.new(Current.account, {
|
||||
type: :team,
|
||||
id: team.id,
|
||||
since: params[:since],
|
||||
until: params[:until]
|
||||
}).summary %>
|
||||
<% row = [ team.name, team_report[:conversations_count], (team_report[:avg_first_response_time]/60).to_i, (team_report[:avg_resolution_time]/60).to_i ] %>
|
||||
<% @report_data.each do |row| %>
|
||||
<%= CSV.generate_line row -%>
|
||||
<% end %>
|
||||
<%= CSV.generate_line [I18n.t('reports.period', since: Date.strptime(params[:since], '%s'), until: Date.strptime(params[:until], '%s'))] %>
|
||||
|
|
|
@ -54,6 +54,17 @@ en:
|
|||
conversations_count: Conversations count
|
||||
avg_first_response_time: Avg first response time (Minutes)
|
||||
avg_resolution_time: Avg resolution time (Minutes)
|
||||
inbox_csv:
|
||||
inbox_name: Inbox name
|
||||
inbox_type: Inbox type
|
||||
conversations_count: No. of conversations
|
||||
avg_first_response_time: Avg first response time (Minutes)
|
||||
avg_resolution_time: Avg resolution time (Minutes)
|
||||
label_csv:
|
||||
label_title: Label
|
||||
conversations_count: No. of conversations
|
||||
avg_first_response_time: Avg first response time (Minutes)
|
||||
avg_resolution_time: Avg resolution time (Minutes)
|
||||
team_csv:
|
||||
team_name: Team name
|
||||
conversations_count: Conversations count
|
||||
|
|
Loading…
Reference in a new issue