feat: Activity messages for team assignments (#1893)
- assignment messages when the conversation team is changed - change assignee based on team - move round-robin and assignee logic to concerns
This commit is contained in:
@@ -90,6 +90,13 @@ class ActionCableListener < BaseListener
|
||||
broadcast(account, tokens, ASSIGNEE_CHANGED, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def team_changed(event)
|
||||
conversation, account = extract_conversation_and_account(event)
|
||||
tokens = user_tokens(account, conversation.inbox.members)
|
||||
|
||||
broadcast(account, tokens, TEAM_CHANGED, conversation.push_event_data)
|
||||
end
|
||||
|
||||
def conversation_contact_changed(event)
|
||||
conversation, account = extract_conversation_and_account(event)
|
||||
tokens = user_tokens(account, conversation.inbox.members)
|
||||
|
||||
77
app/models/concerns/assignment_handler.rb
Normal file
77
app/models/concerns/assignment_handler.rb
Normal file
@@ -0,0 +1,77 @@
|
||||
module AssignmentHandler
|
||||
extend ActiveSupport::Concern
|
||||
include Events::Types
|
||||
|
||||
included do
|
||||
before_save :ensure_assignee_is_from_team
|
||||
after_update :notify_assignment_change, :process_assignment_activities
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_assignee_is_from_team
|
||||
return unless team_id_changed?
|
||||
|
||||
ensure_current_assignee_team
|
||||
self.assignee_id ||= find_team_assignee_id_for_inbox if team&.allow_auto_assign.present?
|
||||
end
|
||||
|
||||
def ensure_current_assignee_team
|
||||
self.assignee_id = nil if team&.members&.exclude?(assignee)
|
||||
end
|
||||
|
||||
def find_team_assignee_id_for_inbox
|
||||
members = inbox.members.ids & team.members.ids
|
||||
# TODO: User round robin to determine the next agent instead of using sample
|
||||
members.sample
|
||||
end
|
||||
|
||||
def notify_assignment_change
|
||||
{
|
||||
ASSIGNEE_CHANGED => -> { saved_change_to_assignee_id? },
|
||||
TEAM_CHANGED => -> { saved_change_to_team_id? }
|
||||
}.each do |event, condition|
|
||||
condition.call && dispatcher_dispatch(event)
|
||||
end
|
||||
end
|
||||
|
||||
def process_assignment_activities
|
||||
user_name = Current.user.name if Current.user.present?
|
||||
if saved_change_to_team_id?
|
||||
create_team_change_activity(user_name)
|
||||
elsif saved_change_to_assignee_id?
|
||||
create_assignee_change_activity(user_name)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_team_change_activity_key
|
||||
key = team_id ? 'assigned' : 'removed'
|
||||
key += '_with_assignee' if key == 'assigned' && saved_change_to_assignee_id? && assignee
|
||||
key
|
||||
end
|
||||
|
||||
def create_team_change_activity(user_name)
|
||||
return unless user_name
|
||||
|
||||
key = generate_team_change_activity_key
|
||||
params = { assignee_name: assignee&.name, team_name: team&.name, user_name: user_name }
|
||||
if key == 'removed'
|
||||
previous_team_id = previous_changes[:team_id][0]
|
||||
params[:team_name] = Team.find_by(id: previous_team_id)&.name if previous_team_id.present?
|
||||
end
|
||||
content = I18n.t("conversations.activity.team.#{key}", **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def create_assignee_change_activity(user_name)
|
||||
return unless user_name
|
||||
|
||||
params = { assignee_name: assignee&.name, user_name: user_name }.compact
|
||||
key = assignee_id ? 'assigned' : 'removed'
|
||||
key = 'self_assigned' if self_assign? assignee_id
|
||||
content = I18n.t("conversations.activity.assignee.#{key}", **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
end
|
||||
26
app/models/concerns/round_robin_handler.rb
Normal file
26
app/models/concerns/round_robin_handler.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module RoundRobinHandler
|
||||
extend ActiveSupport::Concern
|
||||
include Events::Types
|
||||
|
||||
included do
|
||||
after_save :run_round_robin
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_round_robin
|
||||
# Round robin kicks in on conversation create & update
|
||||
# run it only when conversation status changes to open
|
||||
return unless conversation_status_changed_to_open?
|
||||
return unless should_round_robin?
|
||||
|
||||
::RoundRobin::AssignmentService.new(conversation: self).perform
|
||||
end
|
||||
|
||||
def should_round_robin?
|
||||
return false unless inbox.enable_auto_assignment?
|
||||
|
||||
# run only if assignee is blank or doesn't have access to inbox
|
||||
assignee.blank? || inbox.members.exclude?(assignee)
|
||||
end
|
||||
end
|
||||
@@ -36,6 +36,8 @@
|
||||
|
||||
class Conversation < ApplicationRecord
|
||||
include Labelable
|
||||
include AssignmentHandler
|
||||
include RoundRobinHandler
|
||||
|
||||
validates :account_id, presence: true
|
||||
validates :inbox_id, presence: true
|
||||
@@ -62,7 +64,6 @@ class Conversation < ApplicationRecord
|
||||
# reinvestigate in future and identity the implications
|
||||
after_update :notify_status_change, :create_activity
|
||||
after_create_commit :notify_conversation_creation, :queue_conversation_auto_resolution_job
|
||||
after_save :run_round_robin
|
||||
after_commit :set_display_id, unless: :display_id?
|
||||
|
||||
delegate :auto_resolve_duration, to: :account
|
||||
@@ -170,7 +171,6 @@ class Conversation < ApplicationRecord
|
||||
def create_activity
|
||||
user_name = Current.user.name if Current.user.present?
|
||||
status_change_activity(user_name) if saved_change_to_status?
|
||||
create_assignee_change(user_name) if saved_change_to_assignee_id?
|
||||
create_label_change(user_name) if saved_change_to_label_list?
|
||||
end
|
||||
|
||||
@@ -189,7 +189,6 @@ class Conversation < ApplicationRecord
|
||||
CONVERSATION_RESOLVED => -> { saved_change_to_status? && resolved? },
|
||||
CONVERSATION_READ => -> { saved_change_to_contact_last_seen_at? },
|
||||
CONVERSATION_LOCK_TOGGLE => -> { saved_change_to_locked? },
|
||||
ASSIGNEE_CHANGED => -> { saved_change_to_assignee_id? },
|
||||
CONVERSATION_CONTACT_CHANGED => -> { saved_change_to_contact_id? }
|
||||
}.each do |event, condition|
|
||||
condition.call && dispatcher_dispatch(event)
|
||||
@@ -200,28 +199,12 @@ class Conversation < ApplicationRecord
|
||||
Rails.configuration.dispatcher.dispatch(event_name, Time.zone.now, conversation: self)
|
||||
end
|
||||
|
||||
def should_round_robin?
|
||||
return false unless inbox.enable_auto_assignment?
|
||||
|
||||
# run only if assignee is blank or doesn't have access to inbox
|
||||
assignee.blank? || inbox.members.exclude?(assignee)
|
||||
end
|
||||
|
||||
def conversation_status_changed_to_open?
|
||||
return false unless open?
|
||||
# saved_change_to_status? method only works in case of update
|
||||
return true if previous_changes.key?(:id) || saved_change_to_status?
|
||||
end
|
||||
|
||||
def run_round_robin
|
||||
# Round robin kicks in on conversation create & update
|
||||
# run it only when conversation status changes to open
|
||||
return unless conversation_status_changed_to_open?
|
||||
return unless should_round_robin?
|
||||
|
||||
::RoundRobin::AssignmentService.new(conversation: self).perform
|
||||
end
|
||||
|
||||
def create_status_change_message(user_name)
|
||||
content = if user_name
|
||||
I18n.t("conversations.activity.status.#{status}", user_name: user_name)
|
||||
@@ -232,17 +215,6 @@ class Conversation < ApplicationRecord
|
||||
messages.create(activity_message_params(content)) if content
|
||||
end
|
||||
|
||||
def create_assignee_change(user_name)
|
||||
return unless user_name
|
||||
|
||||
params = { assignee_name: assignee&.name, user_name: user_name }.compact
|
||||
key = assignee_id ? 'assigned' : 'removed'
|
||||
key = 'self_assigned' if self_assign? assignee_id
|
||||
content = I18n.t("conversations.activity.assignee.#{key}", **params)
|
||||
|
||||
messages.create(activity_message_params(content))
|
||||
end
|
||||
|
||||
def create_label_change(user_name)
|
||||
return unless user_name
|
||||
|
||||
|
||||
Reference in New Issue
Block a user