feat: Add agents filter in CSAT reports (#4106)

* add agents filter in csat reports
This commit is contained in:
Aswin Dev P.S 2022-03-04 17:19:26 +05:30 committed by GitHub
parent c76b588850
commit b94e67f5d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 14 deletions

View file

@ -30,8 +30,7 @@ class Api::V1::Accounts::CsatSurveyResponsesController < Api::V1::Accounts::Base
def set_csat_survey_responses def set_csat_survey_responses
@csat_survey_responses = filtrate( @csat_survey_responses = filtrate(
Current.account.csat_survey_responses.includes([:conversation, :assigned_agent, :contact]) Current.account.csat_survey_responses.includes([:conversation, :assigned_agent, :contact])
) ).filter_by_created_at(range).filter_by_assigned_agent_id(params[:user_ids])
@csat_survey_responses = @csat_survey_responses.where(created_at: range) if range.present?
end end
def set_current_page_surveys def set_current_page_surveys

View file

@ -6,15 +6,21 @@ class CSATReportsAPI extends ApiClient {
super('csat_survey_responses', { accountScoped: true }); super('csat_survey_responses', { accountScoped: true });
} }
get({ page, from, to } = {}) { get({ page, from, to, user_ids } = {}) {
return axios.get(this.url, { return axios.get(this.url, {
params: { page, since: from, until: to, sort: '-created_at' }, params: {
page,
since: from,
until: to,
sort: '-created_at',
user_ids,
},
}); });
} }
getMetrics({ from, to } = {}) { getMetrics({ from, to, user_ids } = {}) {
return axios.get(`${this.url}/metrics`, { return axios.get(`${this.url}/metrics`, {
params: { since: from, until: to }, params: { since: from, until: to, user_ids },
}); });
} }
} }

View file

@ -333,6 +333,11 @@
"CSAT_REPORTS": { "CSAT_REPORTS": {
"HEADER": "CSAT Reports", "HEADER": "CSAT Reports",
"NO_RECORDS": "There are no CSAT survey responses available.", "NO_RECORDS": "There are no CSAT survey responses available.",
"FILTERS": {
"AGENTS": {
"PLACEHOLDER": "Choose Agents"
}
},
"TABLE": { "TABLE": {
"HEADER": { "HEADER": {
"CONTACT_NAME": "Contact", "CONTACT_NAME": "Contact",

View file

@ -1,6 +1,11 @@
<template> <template>
<div class="column content-box"> <div class="column content-box">
<report-filter-selector @date-range-change="onDateRangeChange" /> <report-filter-selector
agents-filter
:agents-filter-items-list="agentList"
@date-range-change="onDateRangeChange"
@agents-filter-change="onAgentsFilterChange"
/>
<csat-metrics /> <csat-metrics />
<csat-table :page-index="pageIndex" @page-change="onPageNumberChange" /> <csat-table :page-index="pageIndex" @page-change="onPageNumberChange" />
</div> </div>
@ -9,6 +14,7 @@
import CsatMetrics from './components/CsatMetrics'; import CsatMetrics from './components/CsatMetrics';
import CsatTable from './components/CsatTable'; import CsatTable from './components/CsatTable';
import ReportFilterSelector from './components/FilterSelector'; import ReportFilterSelector from './components/FilterSelector';
import { mapGetters } from 'vuex';
export default { export default {
name: 'CsatResponses', name: 'CsatResponses',
@ -18,11 +24,23 @@ export default {
ReportFilterSelector, ReportFilterSelector,
}, },
data() { data() {
return { pageIndex: 1, from: 0, to: 0 }; return { pageIndex: 1, from: 0, to: 0, user_ids: [] };
},
computed: {
...mapGetters({
agentList: 'agents/getAgents',
}),
},
mounted() {
this.$store.dispatch('agents/get');
}, },
methods: { methods: {
getAllData() { getAllData() {
this.$store.dispatch('csat/getMetrics', { from: this.from, to: this.to }); this.$store.dispatch('csat/getMetrics', {
from: this.from,
to: this.to,
user_ids: this.user_ids,
});
this.getResponses(); this.getResponses();
}, },
getResponses() { getResponses() {
@ -30,6 +48,7 @@ export default {
page: this.pageIndex, page: this.pageIndex,
from: this.from, from: this.from,
to: this.to, to: this.to,
user_ids: this.user_ids,
}); });
}, },
onPageNumberChange(pageIndex) { onPageNumberChange(pageIndex) {
@ -41,6 +60,10 @@ export default {
this.to = to; this.to = to;
this.getAllData(); this.getAllData();
}, },
onAgentsFilterChange(agents) {
this.user_ids = agents.map(el => el.id);
this.getAllData();
},
}, },
}; };
</script> </script>

View file

@ -10,6 +10,7 @@
</woot-button> </woot-button>
<report-filter-selector <report-filter-selector
group-by-filter
:selected-group-by-filter="selectedGroupByFilter" :selected-group-by-filter="selectedGroupByFilter"
:filter-items-list="filterItemsList" :filter-items-list="filterItemsList"
@date-range-change="onDateRangeChange" @date-range-change="onDateRangeChange"

View file

@ -24,7 +24,7 @@
@change="onChange" @change="onChange"
/> />
<div <div
v-if="notLast7Days" v-if="notLast7Days && groupByFilter"
class="small-12 medium-3 pull-right margin-left-small" class="small-12 medium-3 pull-right margin-left-small"
> >
<p aria-hidden="true" class="hide"> <p aria-hidden="true" class="hide">
@ -41,6 +41,26 @@
@input="changeFilterSelection" @input="changeFilterSelection"
/> />
</div> </div>
<div
v-if="agentsFilter"
class="small-12 medium-3 pull-right margin-left-small"
>
<multiselect
v-model="selectedAgents"
:options="agentsFilterItemsList"
track-by="id"
label="name"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:hide-selected="true"
:placeholder="$t('CSAT_REPORTS.FILTERS.AGENTS.PLACEHOLDER')"
selected-label
:select-label="$t('FORMS.MULTISELECT.ENTER_TO_SELECT')"
:deselect-label="$t('FORMS.MULTISELECT.ENTER_TO_REMOVE')"
@input="handleAgentsFilterSelection"
/>
</div>
</div> </div>
</template> </template>
<script> <script>
@ -61,10 +81,22 @@ export default {
type: Array, type: Array,
default: () => [], default: () => [],
}, },
agentsFilterItemsList: {
type: Array,
default: () => [],
},
selectedGroupByFilter: { selectedGroupByFilter: {
type: Object, type: Object,
default: () => {}, default: () => {},
}, },
groupByFilter: {
type: Boolean,
default: false,
},
agentsFilter: {
type: Boolean,
default: false,
},
}, },
data() { data() {
return { return {
@ -72,6 +104,7 @@ export default {
dateRange: this.$t('REPORT.DATE_RANGE'), dateRange: this.$t('REPORT.DATE_RANGE'),
customDateRange: [new Date(), new Date()], customDateRange: [new Date(), new Date()],
currentSelectedFilter: null, currentSelectedFilter: null,
selectedAgents: [],
}; };
}, },
computed: { computed: {
@ -149,6 +182,9 @@ export default {
changeFilterSelection() { changeFilterSelection() {
this.$emit('filter-change', this.currentSelectedFilter); this.$emit('filter-change', this.currentSelectedFilter);
}, },
handleAgentsFilterSelection() {
this.$emit('agents-filter-change', this.selectedAgents);
},
}, },
}; };
</script> </script>

View file

@ -82,10 +82,13 @@ export const getters = {
}; };
export const actions = { export const actions = {
get: async function getResponses({ commit }, { page = 1, from, to } = {}) { get: async function getResponses(
{ commit },
{ page = 1, from, to, user_ids } = {}
) {
commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetching: true }); commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetching: true });
try { try {
const response = await CSATReports.get({ page, from, to }); const response = await CSATReports.get({ page, from, to, user_ids });
commit(types.SET_CSAT_RESPONSE, response.data); commit(types.SET_CSAT_RESPONSE, response.data);
} catch (error) { } catch (error) {
// Ignore error // Ignore error
@ -93,10 +96,10 @@ export const actions = {
commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetching: false }); commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetching: false });
} }
}, },
getMetrics: async function getMetrics({ commit }, { from, to }) { getMetrics: async function getMetrics({ commit }, { from, to, user_ids }) {
commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetchingMetrics: true }); commit(types.SET_CSAT_RESPONSE_UI_FLAG, { isFetchingMetrics: true });
try { try {
const response = await CSATReports.getMetrics({ from, to }); const response = await CSATReports.getMetrics({ from, to, user_ids });
commit(types.SET_CSAT_RESPONSE_METRICS, response.data); commit(types.SET_CSAT_RESPONSE_METRICS, response.data);
} catch (error) { } catch (error) {
// Ignore error // Ignore error

View file

@ -40,4 +40,7 @@ class CsatSurveyResponse < ApplicationRecord
validates :account_id, presence: true validates :account_id, presence: true
validates :contact_id, presence: true validates :contact_id, presence: true
validates :conversation_id, presence: true validates :conversation_id, presence: true
scope :filter_by_created_at, ->(range) { where(created_at: range) if range.present? }
scope :filter_by_assigned_agent_id, ->(user_ids) { where(assigned_agent_id: user_ids) if user_ids.present? }
end end

View file

@ -48,6 +48,25 @@ RSpec.describe 'CSAT Survey Responses API', type: :request do
expect(response_data.pluck('id')).not_to include(csat_10_days_ago.id) expect(response_data.pluck('id')).not_to include(csat_10_days_ago.id)
end end
it 'filters csat responses based on a date range and agent ids' do
csat1_assigned_agent = create(:user, account: account, role: :agent)
csat2_assigned_agent = create(:user, account: account, role: :agent)
create(:csat_survey_response, account: account, created_at: 10.days.ago, assigned_agent: csat1_assigned_agent)
create(:csat_survey_response, account: account, created_at: 3.days.ago, assigned_agent: csat2_assigned_agent)
create(:csat_survey_response, account: account, created_at: 5.days.ago)
get "/api/v1/accounts/#{account.id}/csat_survey_responses",
params: { since: 11.days.ago.to_time.to_i.to_s, until: Time.zone.today.to_time.to_i.to_s,
user_ids: [csat1_assigned_agent.id, csat2_assigned_agent.id] },
headers: administrator.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
response_data = JSON.parse(response.body)
expect(response_data.size).to eq 2
end
it 'returns csat responses even if the agent is deleted from account' do it 'returns csat responses even if the agent is deleted from account' do
deleted_agent_csat = create(:csat_survey_response, account: account, assigned_agent: agent) deleted_agent_csat = create(:csat_survey_response, account: account, assigned_agent: agent)
deleted_agent_csat.assigned_agent.account_users.destroy_all deleted_agent_csat.assigned_agent.account_users.destroy_all
@ -106,6 +125,27 @@ RSpec.describe 'CSAT Survey Responses API', type: :request do
expect(response_data['total_sent_messages_count']).to eq 0 expect(response_data['total_sent_messages_count']).to eq 0
expect(response_data['ratings_count']).to eq({ '1' => 1 }) expect(response_data['ratings_count']).to eq({ '1' => 1 })
end end
it 'filters csat metrics based on a date range and agent ids' do
csat1_assigned_agent = create(:user, account: account, role: :agent)
csat2_assigned_agent = create(:user, account: account, role: :agent)
create(:csat_survey_response, account: account, created_at: 10.days.ago, assigned_agent: csat1_assigned_agent)
create(:csat_survey_response, account: account, created_at: 3.days.ago, assigned_agent: csat2_assigned_agent)
create(:csat_survey_response, account: account, created_at: 5.days.ago)
get "/api/v1/accounts/#{account.id}/csat_survey_responses/metrics",
params: { since: 11.days.ago.to_time.to_i.to_s, until: Time.zone.today.to_time.to_i.to_s,
user_ids: [csat1_assigned_agent.id, csat2_assigned_agent.id] },
headers: administrator.create_new_auth_token,
as: :json
expect(response).to have_http_status(:success)
response_data = JSON.parse(response.body)
expect(response_data['total_count']).to eq 2
expect(response_data['total_sent_messages_count']).to eq 0
expect(response_data['ratings_count']).to eq({ '1' => 2 })
end
end end
end end
end end