feat: Add CSAT response APIs (#2503)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
28
app/builders/csat_surveys/response_builder.rb
Normal file
28
app/builders/csat_surveys/response_builder.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
class CsatSurveys::ResponseBuilder
|
||||
pattr_initialize [:message]
|
||||
|
||||
def perform
|
||||
raise 'Invalid Message' unless message.input_csat?
|
||||
|
||||
conversation = message.conversation
|
||||
rating = message.content_attributes.dig('submitted_values', 'csat_survey_response', 'rating')
|
||||
feedback_message = message.content_attributes.dig('submitted_values', 'csat_survey_response', 'feedback_message')
|
||||
|
||||
return if rating.blank?
|
||||
|
||||
process_csat_response(conversation, rating, feedback_message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_csat_response(conversation, rating, feedback_message)
|
||||
csat_survey_response = message.csat_survey_response || CsatSurveyResponse.new(
|
||||
message_id: message.id, account_id: message.account_id, conversation_id: message.conversation_id,
|
||||
contact_id: conversation.contact_id, assigned_agent: conversation.assignee
|
||||
)
|
||||
csat_survey_response.rating = rating
|
||||
csat_survey_response.feedback_message = feedback_message
|
||||
csat_survey_response.save!
|
||||
csat_survey_response
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,5 @@
|
||||
class V2::ReportBuilder
|
||||
include DateRangeHelper
|
||||
attr_reader :account, :params
|
||||
|
||||
def initialize(account, params)
|
||||
@@ -83,10 +84,6 @@ class V2::ReportBuilder
|
||||
.average(:value)
|
||||
end
|
||||
|
||||
def range
|
||||
parse_date_time(params[:since])..parse_date_time(params[:until])
|
||||
end
|
||||
|
||||
# Taking average of average is not too accurate
|
||||
# https://en.wikipedia.org/wiki/Simpson's_paradox
|
||||
# TODO: Will optimize this later
|
||||
@@ -101,11 +98,4 @@ class V2::ReportBuilder
|
||||
|
||||
(avg_first_response_time.values.sum / avg_first_response_time.values.length)
|
||||
end
|
||||
|
||||
def parse_date_time(datetime)
|
||||
return datetime if datetime.is_a?(DateTime)
|
||||
return datetime.to_datetime if datetime.is_a?(Time) || datetime.is_a?(Date)
|
||||
|
||||
DateTime.strptime(datetime, '%s')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
class Api::V1::Accounts::CsatSurveyResponsesController < Api::V1::Accounts::BaseController
|
||||
include DateRangeHelper
|
||||
|
||||
RESULTS_PER_PAGE = 25
|
||||
|
||||
before_action :check_authorization
|
||||
before_action :csat_survey_responses, only: [:index]
|
||||
|
||||
def index; end
|
||||
|
||||
private
|
||||
|
||||
def csat_survey_responses
|
||||
@csat_survey_responses = Current.account.csat_survey_responses
|
||||
@csat_survey_responses = @csat_survey_responses.where(created_at: range) if range.present?
|
||||
@csat_survey_responses.page(@current_page).per(RESULTS_PER_PAGE)
|
||||
end
|
||||
|
||||
def set_current_page
|
||||
@current_page = params[:page] || 1
|
||||
end
|
||||
end
|
||||
@@ -54,7 +54,7 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
||||
end
|
||||
|
||||
def message_update_params
|
||||
params.permit(message: [{ submitted_values: [:name, :title, :value, { csat_survey_response: [:feedback, :rating] }] }])
|
||||
params.permit(message: [{ submitted_values: [:name, :title, :value, { csat_survey_response: [:feedback_message, :rating] }] }])
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
|
||||
@@ -10,10 +10,12 @@ class AsyncDispatcher < BaseDispatcher
|
||||
|
||||
def listeners
|
||||
[
|
||||
CampaignListener.instance,
|
||||
CsatSurveyListener.instance,
|
||||
EventListener.instance,
|
||||
WebhookListener.instance,
|
||||
InstallationWebhookListener.instance, HookListener.instance,
|
||||
CampaignListener.instance
|
||||
HookListener.instance,
|
||||
InstallationWebhookListener.instance,
|
||||
WebhookListener.instance
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
19
app/helpers/date_range_helper.rb
Normal file
19
app/helpers/date_range_helper.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
##############################################
|
||||
# Helpers to implement date range filtering to APIs
|
||||
# Include in your controller or service class where params is available
|
||||
##############################################
|
||||
|
||||
module DateRangeHelper
|
||||
def range
|
||||
return if params[:since].blank? || params[:until].blank?
|
||||
|
||||
parse_date_time(params[:since])..parse_date_time(params[:until])
|
||||
end
|
||||
|
||||
def parse_date_time(datetime)
|
||||
return datetime if datetime.is_a?(DateTime)
|
||||
return datetime.to_datetime if datetime.is_a?(Time) || datetime.is_a?(Date)
|
||||
|
||||
DateTime.strptime(datetime, '%s')
|
||||
end
|
||||
end
|
||||
@@ -43,7 +43,7 @@ const generateCSATContent = (
|
||||
const {
|
||||
submitted_values: { csat_survey_response: surveyResponse = {} } = {},
|
||||
} = contentAttributes;
|
||||
const { rating, feedback } = surveyResponse || {};
|
||||
const { rating, feedback_message } = surveyResponse || {};
|
||||
|
||||
let messageContent = '';
|
||||
if (rating) {
|
||||
@@ -53,9 +53,9 @@ const generateCSATContent = (
|
||||
messageContent += `<div><strong>${ratingTitle}</strong></div>`;
|
||||
messageContent += `<p>${ratingObject.emoji}</p>`;
|
||||
}
|
||||
if (feedback) {
|
||||
if (feedback_message) {
|
||||
messageContent += `<div><strong>${feedbackTitle}</strong></div>`;
|
||||
messageContent += `<p>${feedback}</p>`;
|
||||
messageContent += `<p>${feedback_message}</p>`;
|
||||
}
|
||||
return messageContent;
|
||||
};
|
||||
|
||||
@@ -21,7 +21,10 @@ describe('#generateBotMessageContent', () => {
|
||||
expect(
|
||||
generateBotMessageContent('input_csat', {
|
||||
submitted_values: {
|
||||
csat_survey_response: { rating: 5, feedback: 'Great Service' },
|
||||
csat_survey_response: {
|
||||
rating: 5,
|
||||
feedback_message: 'Great Service',
|
||||
},
|
||||
},
|
||||
})
|
||||
).toEqual(
|
||||
@@ -33,7 +36,7 @@ describe('#generateBotMessageContent', () => {
|
||||
'input_csat',
|
||||
{
|
||||
submitted_values: {
|
||||
csat_survey_response: { rating: 1, feedback: '' },
|
||||
csat_survey_response: { rating: 1, feedback_message: '' },
|
||||
},
|
||||
},
|
||||
{ csat: { ratingTitle: 'റേറ്റിംഗ്', feedbackTitle: 'പ്രതികരണം' } }
|
||||
|
||||
@@ -130,7 +130,7 @@ export default {
|
||||
submittedValues: {
|
||||
csat_survey_response: {
|
||||
rating,
|
||||
feedback,
|
||||
feedback_message: feedback,
|
||||
},
|
||||
},
|
||||
messageId: this.messageId,
|
||||
|
||||
8
app/listeners/csat_survey_listener.rb
Normal file
8
app/listeners/csat_survey_listener.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class CsatSurveyListener < BaseListener
|
||||
def message_updated(event)
|
||||
message = extract_message_and_account(event)[0]
|
||||
return unless message.input_csat?
|
||||
|
||||
CsatSurveys::ResponseBuilder.new(message: message).perform
|
||||
end
|
||||
end
|
||||
@@ -34,6 +34,7 @@ class Account < ApplicationRecord
|
||||
has_many :account_users, dependent: :destroy
|
||||
has_many :agent_bot_inboxes, dependent: :destroy
|
||||
has_many :agent_bots, dependent: :destroy
|
||||
has_many :csat_survey_responses, dependent: :destroy
|
||||
has_many :data_imports, dependent: :destroy
|
||||
has_many :users, through: :account_users
|
||||
has_many :inboxes, dependent: :destroy
|
||||
|
||||
@@ -39,6 +39,7 @@ class Contact < ApplicationRecord
|
||||
belongs_to :account
|
||||
has_many :conversations, dependent: :destroy
|
||||
has_many :contact_inboxes, dependent: :destroy
|
||||
has_many :csat_survey_responses, dependent: :destroy
|
||||
has_many :inboxes, through: :contact_inboxes
|
||||
has_many :messages, as: :sender, dependent: :destroy
|
||||
has_many :notes, dependent: :destroy
|
||||
|
||||
@@ -60,6 +60,7 @@ class Conversation < ApplicationRecord
|
||||
belongs_to :campaign, optional: true
|
||||
|
||||
has_many :messages, dependent: :destroy, autosave: true
|
||||
has_one :csat_survey_response, dependent: :destroy
|
||||
|
||||
before_create :set_bot_conversation
|
||||
|
||||
|
||||
43
app/models/csat_survey_response.rb
Normal file
43
app/models/csat_survey_response.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: csat_survey_responses
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# feedback_message :text
|
||||
# rating :integer not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
# assigned_agent_id :bigint
|
||||
# contact_id :bigint not null
|
||||
# conversation_id :bigint not null
|
||||
# message_id :bigint not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_csat_survey_responses_on_account_id (account_id)
|
||||
# index_csat_survey_responses_on_assigned_agent_id (assigned_agent_id)
|
||||
# index_csat_survey_responses_on_contact_id (contact_id)
|
||||
# index_csat_survey_responses_on_conversation_id (conversation_id)
|
||||
# index_csat_survey_responses_on_message_id (message_id) UNIQUE
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (account_id => accounts.id)
|
||||
# fk_rails_... (assigned_agent_id => users.id)
|
||||
# fk_rails_... (contact_id => contacts.id)
|
||||
# fk_rails_... (conversation_id => conversations.id)
|
||||
# fk_rails_... (message_id => messages.id)
|
||||
#
|
||||
class CsatSurveyResponse < ApplicationRecord
|
||||
belongs_to :account
|
||||
belongs_to :conversation
|
||||
belongs_to :contact
|
||||
belongs_to :message
|
||||
belongs_to :assigned_agent, class_name: 'User', optional: true
|
||||
|
||||
validates :rating, presence: true, inclusion: { in: [1, 2, 3, 4, 5] }
|
||||
validates :account_id, presence: true
|
||||
validates :contact_id, presence: true
|
||||
validates :conversation_id, presence: true
|
||||
end
|
||||
@@ -79,6 +79,7 @@ class Message < ApplicationRecord
|
||||
belongs_to :sender, polymorphic: true, required: false
|
||||
|
||||
has_many :attachments, dependent: :destroy, autosave: true, before_add: :validate_attachments_limit
|
||||
has_one :csat_survey_response, dependent: :destroy
|
||||
|
||||
after_create :reopen_conversation,
|
||||
:notify_via_mail
|
||||
|
||||
@@ -71,6 +71,7 @@ class User < ApplicationRecord
|
||||
|
||||
has_many :assigned_conversations, foreign_key: 'assignee_id', class_name: 'Conversation', dependent: :nullify
|
||||
alias_attribute :conversations, :assigned_conversations
|
||||
has_many :csat_survey_responses, foreign_key: 'assigned_agent_id', dependent: :nullify
|
||||
|
||||
has_many :inbox_members, dependent: :destroy
|
||||
has_many :inboxes, through: :inbox_members, source: :inbox
|
||||
|
||||
5
app/policies/csat_survey_response_policy.rb
Normal file
5
app/policies/csat_survey_response_policy.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class CsatSurveyResponsePolicy < ApplicationPolicy
|
||||
def index?
|
||||
@account_user.administrator?
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
json.array! @csat_survey_responses do |csat_survey_response|
|
||||
json.partial! 'api/v1/models/csat_survey_response.json.jbuilder', resource: csat_survey_response
|
||||
end
|
||||
@@ -0,0 +1,9 @@
|
||||
json.id resource.id
|
||||
json.rating resource.rating
|
||||
json.feedback_message resource.feedback_message
|
||||
json.account_id resource.account_id
|
||||
json.message_id resource.message_id
|
||||
json.contact resource.contact
|
||||
json.conversation_id resource.conversation.display_id
|
||||
json.assigned_agent resource.assigned_agent
|
||||
json.created_at resource.created_at
|
||||
Reference in New Issue
Block a user