mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 11:08:04 +00:00 
			
		
		
		
	feat: Add agents filter in CSAT reports (#4106)
* add agents filter in csat reports
This commit is contained in:
		| @@ -30,8 +30,7 @@ class Api::V1::Accounts::CsatSurveyResponsesController < Api::V1::Accounts::Base | ||||
|   def set_csat_survey_responses | ||||
|     @csat_survey_responses = filtrate( | ||||
|       Current.account.csat_survey_responses.includes([:conversation, :assigned_agent, :contact]) | ||||
|     ) | ||||
|     @csat_survey_responses = @csat_survey_responses.where(created_at: range) if range.present? | ||||
|     ).filter_by_created_at(range).filter_by_assigned_agent_id(params[:user_ids]) | ||||
|   end | ||||
|  | ||||
|   def set_current_page_surveys | ||||
|   | ||||
| @@ -6,15 +6,21 @@ class CSATReportsAPI extends ApiClient { | ||||
|     super('csat_survey_responses', { accountScoped: true }); | ||||
|   } | ||||
|  | ||||
|   get({ page, from, to } = {}) { | ||||
|   get({ page, from, to, user_ids } = {}) { | ||||
|     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`, { | ||||
|       params: { since: from, until: to }, | ||||
|       params: { since: from, until: to, user_ids }, | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -333,6 +333,11 @@ | ||||
|   "CSAT_REPORTS": { | ||||
|     "HEADER": "CSAT Reports", | ||||
|     "NO_RECORDS": "There are no CSAT survey responses available.", | ||||
|     "FILTERS": { | ||||
|       "AGENTS": { | ||||
|         "PLACEHOLDER": "Choose Agents" | ||||
|       } | ||||
|     }, | ||||
|     "TABLE": { | ||||
|       "HEADER": { | ||||
|         "CONTACT_NAME": "Contact", | ||||
|   | ||||
| @@ -1,6 +1,11 @@ | ||||
| <template> | ||||
|   <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-table :page-index="pageIndex" @page-change="onPageNumberChange" /> | ||||
|   </div> | ||||
| @@ -9,6 +14,7 @@ | ||||
| import CsatMetrics from './components/CsatMetrics'; | ||||
| import CsatTable from './components/CsatTable'; | ||||
| import ReportFilterSelector from './components/FilterSelector'; | ||||
| import { mapGetters } from 'vuex'; | ||||
|  | ||||
| export default { | ||||
|   name: 'CsatResponses', | ||||
| @@ -18,11 +24,23 @@ export default { | ||||
|     ReportFilterSelector, | ||||
|   }, | ||||
|   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: { | ||||
|     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(); | ||||
|     }, | ||||
|     getResponses() { | ||||
| @@ -30,6 +48,7 @@ export default { | ||||
|         page: this.pageIndex, | ||||
|         from: this.from, | ||||
|         to: this.to, | ||||
|         user_ids: this.user_ids, | ||||
|       }); | ||||
|     }, | ||||
|     onPageNumberChange(pageIndex) { | ||||
| @@ -41,6 +60,10 @@ export default { | ||||
|       this.to = to; | ||||
|       this.getAllData(); | ||||
|     }, | ||||
|     onAgentsFilterChange(agents) { | ||||
|       this.user_ids = agents.map(el => el.id); | ||||
|       this.getAllData(); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|     </woot-button> | ||||
|  | ||||
|     <report-filter-selector | ||||
|       group-by-filter | ||||
|       :selected-group-by-filter="selectedGroupByFilter" | ||||
|       :filter-items-list="filterItemsList" | ||||
|       @date-range-change="onDateRangeChange" | ||||
|   | ||||
| @@ -24,7 +24,7 @@ | ||||
|       @change="onChange" | ||||
|     /> | ||||
|     <div | ||||
|       v-if="notLast7Days" | ||||
|       v-if="notLast7Days && groupByFilter" | ||||
|       class="small-12 medium-3 pull-right margin-left-small" | ||||
|     > | ||||
|       <p aria-hidden="true" class="hide"> | ||||
| @@ -41,6 +41,26 @@ | ||||
|         @input="changeFilterSelection" | ||||
|       /> | ||||
|     </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> | ||||
| </template> | ||||
| <script> | ||||
| @@ -61,10 +81,22 @@ export default { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|     agentsFilterItemsList: { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|     selectedGroupByFilter: { | ||||
|       type: Object, | ||||
|       default: () => {}, | ||||
|     }, | ||||
|     groupByFilter: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|     }, | ||||
|     agentsFilter: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
| @@ -72,6 +104,7 @@ export default { | ||||
|       dateRange: this.$t('REPORT.DATE_RANGE'), | ||||
|       customDateRange: [new Date(), new Date()], | ||||
|       currentSelectedFilter: null, | ||||
|       selectedAgents: [], | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
| @@ -149,6 +182,9 @@ export default { | ||||
|     changeFilterSelection() { | ||||
|       this.$emit('filter-change', this.currentSelectedFilter); | ||||
|     }, | ||||
|     handleAgentsFilterSelection() { | ||||
|       this.$emit('agents-filter-change', this.selectedAgents); | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|   | ||||
| @@ -82,10 +82,13 @@ export const getters = { | ||||
| }; | ||||
|  | ||||
| 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 }); | ||||
|     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); | ||||
|     } catch (error) { | ||||
|       // Ignore error | ||||
| @@ -93,10 +96,10 @@ export const actions = { | ||||
|       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 }); | ||||
|     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); | ||||
|     } catch (error) { | ||||
|       // Ignore error | ||||
|   | ||||
| @@ -40,4 +40,7 @@ class CsatSurveyResponse < ApplicationRecord | ||||
|   validates :account_id, presence: true | ||||
|   validates :contact_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 | ||||
|   | ||||
| @@ -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) | ||||
|       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 | ||||
|         deleted_agent_csat = create(:csat_survey_response, account: account, assigned_agent: agent) | ||||
|         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['ratings_count']).to eq({ '1' => 1 }) | ||||
|       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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Aswin Dev P.S
					Aswin Dev P.S