diff --git a/app/javascript/dashboard/composables/spec/useAutomation.spec.js b/app/javascript/dashboard/composables/spec/useAutomation.spec.js index 2e07f1671..e90d9ef53 100644 --- a/app/javascript/dashboard/composables/spec/useAutomation.spec.js +++ b/app/javascript/dashboard/composables/spec/useAutomation.spec.js @@ -92,7 +92,9 @@ describe('useAutomation', () => { case 'assign_team': return teams; case 'assign_agent': - return agents; + return options.addNoneToListFn + ? options.addNoneToListFn(options.agents) + : options.agents; case 'send_email_to_team': return teams; case 'send_message': @@ -240,7 +242,11 @@ describe('useAutomation', () => { expect(getActionDropdownValues('add_label')).toEqual(labels); expect(getActionDropdownValues('assign_team')).toEqual(teams); - expect(getActionDropdownValues('assign_agent')).toEqual(agents); + expect(getActionDropdownValues('assign_agent')).toEqual([ + { id: 'nil', name: 'AUTOMATION.NONE_OPTION' }, + { id: 'last_responding_agent', name: 'AUTOMATION.LAST_RESPONDING_AGENT' }, + ...agents, + ]); expect(getActionDropdownValues('send_email_to_team')).toEqual(teams); expect(getActionDropdownValues('send_message')).toEqual([]); expect(getActionDropdownValues('add_sla')).toEqual(slaPolicies); diff --git a/app/javascript/dashboard/composables/spec/useEditableAutomation.spec.js b/app/javascript/dashboard/composables/spec/useEditableAutomation.spec.js index c6177e9a2..42ba92bee 100644 --- a/app/javascript/dashboard/composables/spec/useEditableAutomation.spec.js +++ b/app/javascript/dashboard/composables/spec/useEditableAutomation.spec.js @@ -16,7 +16,20 @@ describe('useEditableAutomation', () => { return []; }), - getActionDropdownValues: vi.fn(), + getActionDropdownValues: vi.fn(actionName => { + if (actionName === 'assign_agent') { + return [ + { id: 'nil', name: 'None' }, + { + id: 'last_responding_agent', + name: 'Last Responding Agent', + }, + { id: 1, name: 'Agent 1' }, + ]; + } + + return []; + }), }); }); @@ -51,4 +64,35 @@ describe('useEditableAutomation', () => { }, ]); }); + + it('rehydrates last responding agent as a selected action option', () => { + const automation = { + event_name: 'conversation_created', + conditions: [], + actions: [ + { + action_name: 'assign_agent', + action_params: ['last_responding_agent'], + }, + ], + }; + const automationActionTypes = [ + { key: 'assign_agent', inputType: 'search_select' }, + ]; + + const { formatAutomation } = useEditableAutomation(); + const result = formatAutomation(automation, [], {}, automationActionTypes); + + expect(result.actions).toEqual([ + { + action_name: 'assign_agent', + action_params: [ + { + id: 'last_responding_agent', + name: 'Last Responding Agent', + }, + ], + }, + ]); + }); }); diff --git a/app/javascript/dashboard/composables/useAutomationValues.js b/app/javascript/dashboard/composables/useAutomationValues.js index 709aba20b..69f87d427 100644 --- a/app/javascript/dashboard/composables/useAutomationValues.js +++ b/app/javascript/dashboard/composables/useAutomationValues.js @@ -121,8 +121,19 @@ export default function useAutomationValues() { * @returns {Array} An array of action dropdown values. */ const getActionDropdownValues = type => { + let agentsList = agents.value; + if (type === 'assign_agent') { + agentsList = [ + { + id: 'last_responding_agent', + name: t('AUTOMATION.LAST_RESPONDING_AGENT'), + }, + ...agentsList, + ]; + } + return getActionOptions({ - agents: agents.value, + agents: agentsList, labels: labels.value, teams: teams.value, slaPolicies: slaPolicies.value, diff --git a/app/javascript/dashboard/i18n/locale/en/automation.json b/app/javascript/dashboard/i18n/locale/en/automation.json index 46f4a520e..e96a28b40 100644 --- a/app/javascript/dashboard/i18n/locale/en/automation.json +++ b/app/javascript/dashboard/i18n/locale/en/automation.json @@ -130,6 +130,7 @@ "ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required" }, "NONE_OPTION": "None", + "LAST_RESPONDING_AGENT": "Last Responding Agent", "EVENTS": { "CONVERSATION_CREATED": "Conversation Created", "CONVERSATION_UPDATED": "Conversation Updated", diff --git a/app/services/action_service.rb b/app/services/action_service.rb index fa0d1a7fc..27f513f24 100644 --- a/app/services/action_service.rb +++ b/app/services/action_service.rb @@ -43,10 +43,10 @@ class ActionService def assign_agent(agent_ids = []) return @conversation.update!(assignee_id: nil) if agent_ids[0] == 'nil' + agent_ids = [last_responding_agent_id] if agent_ids[0] == 'last_responding_agent' return unless agent_belongs_to_inbox?(agent_ids) @agent = @account.users.find_by(id: agent_ids) - return unless @agent.present? && @agent.confirmed? @conversation.update!(assignee_id: @agent.id) @@ -95,6 +95,10 @@ class ActionService private + def last_responding_agent_id + @conversation.messages.outgoing.where(sender_type: 'User', private: false).last&.sender_id + end + def agent_belongs_to_inbox?(agent_ids) member_ids = @conversation.inbox.members.pluck(:user_id) assignable_agent_ids = member_ids + @account.administrators.ids diff --git a/spec/services/action_service_spec.rb b/spec/services/action_service_spec.rb index 6742b85fb..99ac2afc7 100644 --- a/spec/services/action_service_spec.rb +++ b/spec/services/action_service_spec.rb @@ -70,6 +70,33 @@ describe ActionService do expect(conversation.reload.assignee).to eq(original_assignee) end end + + context 'when assigning the last responding agent' do + it 'assigns the last agent who replied publicly' do + note_author = create(:user, account: account, role: :agent) + inbox_member + create(:inbox_member, inbox: conversation.inbox, user: note_author) + create(:message, message_type: :outgoing, account: account, + inbox: conversation.inbox, conversation: conversation, sender: agent) + create(:message, message_type: :outgoing, private: true, account: account, + inbox: conversation.inbox, conversation: conversation, sender: note_author) + + action_service.assign_agent(['last_responding_agent']) + + expect(conversation.reload.assignee).to eq(agent) + end + + it 'does not assign the conversation when there is no public agent reply' do + inbox_member + original_assignee = conversation.assignee + create(:message, message_type: :outgoing, private: true, account: account, + inbox: conversation.inbox, conversation: conversation, sender: agent) + + action_service.assign_agent(['last_responding_agent']) + + expect(conversation.reload.assignee).to eq(original_assignee) + end + end end describe '#assign_team' do diff --git a/spec/services/automation_rules/action_service_spec.rb b/spec/services/automation_rules/action_service_spec.rb index 0667726ff..f24617d29 100644 --- a/spec/services/automation_rules/action_service_spec.rb +++ b/spec/services/automation_rules/action_service_spec.rb @@ -201,5 +201,21 @@ RSpec.describe AutomationRules::ActionService do described_class.new(rule, account, conversation).perform end end + + describe '#perform with assign_agent action' do + before do + create(:inbox_member, inbox: conversation.inbox, user: agent) + rule.actions << { action_name: 'assign_agent', action_params: ['last_responding_agent'] } + end + + it 'assigns the conversation to the last responding agent' do + create(:message, message_type: :outgoing, account: account, + inbox: conversation.inbox, conversation: conversation, sender: agent) + + described_class.new(rule, account, conversation).perform + + expect(conversation.reload.assignee).to eq(agent) + end + end end end