feat: Introduce last responding agent option to automation assign agent (#12326)
Introduce a `Last Responding Agent` options to assign_agents action in automations to cover the following use cases. - Assign conversations to first responding agent : ( automation message created at , if assignee is nil, assign last responding agent ) - Ensure conversations are not resolved with out an assignee : ( automation conversation resolved at : if assignee is nil, assign last responding agent ) and potential other cases. fixes: #1592
This commit is contained in:
@@ -92,7 +92,9 @@ describe('useAutomation', () => {
|
|||||||
case 'assign_team':
|
case 'assign_team':
|
||||||
return teams;
|
return teams;
|
||||||
case 'assign_agent':
|
case 'assign_agent':
|
||||||
return agents;
|
return options.addNoneToListFn
|
||||||
|
? options.addNoneToListFn(options.agents)
|
||||||
|
: options.agents;
|
||||||
case 'send_email_to_team':
|
case 'send_email_to_team':
|
||||||
return teams;
|
return teams;
|
||||||
case 'send_message':
|
case 'send_message':
|
||||||
@@ -240,7 +242,11 @@ describe('useAutomation', () => {
|
|||||||
|
|
||||||
expect(getActionDropdownValues('add_label')).toEqual(labels);
|
expect(getActionDropdownValues('add_label')).toEqual(labels);
|
||||||
expect(getActionDropdownValues('assign_team')).toEqual(teams);
|
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_email_to_team')).toEqual(teams);
|
||||||
expect(getActionDropdownValues('send_message')).toEqual([]);
|
expect(getActionDropdownValues('send_message')).toEqual([]);
|
||||||
expect(getActionDropdownValues('add_sla')).toEqual(slaPolicies);
|
expect(getActionDropdownValues('add_sla')).toEqual(slaPolicies);
|
||||||
|
|||||||
@@ -16,7 +16,20 @@ describe('useEditableAutomation', () => {
|
|||||||
|
|
||||||
return [];
|
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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -121,8 +121,19 @@ export default function useAutomationValues() {
|
|||||||
* @returns {Array} An array of action dropdown values.
|
* @returns {Array} An array of action dropdown values.
|
||||||
*/
|
*/
|
||||||
const getActionDropdownValues = type => {
|
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({
|
return getActionOptions({
|
||||||
agents: agents.value,
|
agents: agentsList,
|
||||||
labels: labels.value,
|
labels: labels.value,
|
||||||
teams: teams.value,
|
teams: teams.value,
|
||||||
slaPolicies: slaPolicies.value,
|
slaPolicies: slaPolicies.value,
|
||||||
|
|||||||
@@ -130,6 +130,7 @@
|
|||||||
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
|
"ATLEAST_ONE_ACTION_REQUIRED": "At least one action is required"
|
||||||
},
|
},
|
||||||
"NONE_OPTION": "None",
|
"NONE_OPTION": "None",
|
||||||
|
"LAST_RESPONDING_AGENT": "Last Responding Agent",
|
||||||
"EVENTS": {
|
"EVENTS": {
|
||||||
"CONVERSATION_CREATED": "Conversation Created",
|
"CONVERSATION_CREATED": "Conversation Created",
|
||||||
"CONVERSATION_UPDATED": "Conversation Updated",
|
"CONVERSATION_UPDATED": "Conversation Updated",
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ class ActionService
|
|||||||
def assign_agent(agent_ids = [])
|
def assign_agent(agent_ids = [])
|
||||||
return @conversation.update!(assignee_id: nil) if agent_ids[0] == 'nil'
|
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)
|
return unless agent_belongs_to_inbox?(agent_ids)
|
||||||
|
|
||||||
@agent = @account.users.find_by(id: agent_ids)
|
@agent = @account.users.find_by(id: agent_ids)
|
||||||
|
|
||||||
return unless @agent.present? && @agent.confirmed?
|
return unless @agent.present? && @agent.confirmed?
|
||||||
|
|
||||||
@conversation.update!(assignee_id: @agent.id)
|
@conversation.update!(assignee_id: @agent.id)
|
||||||
@@ -95,6 +95,10 @@ class ActionService
|
|||||||
|
|
||||||
private
|
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)
|
def agent_belongs_to_inbox?(agent_ids)
|
||||||
member_ids = @conversation.inbox.members.pluck(:user_id)
|
member_ids = @conversation.inbox.members.pluck(:user_id)
|
||||||
assignable_agent_ids = member_ids + @account.administrators.ids
|
assignable_agent_ids = member_ids + @account.administrators.ids
|
||||||
|
|||||||
@@ -70,6 +70,33 @@ describe ActionService do
|
|||||||
expect(conversation.reload.assignee).to eq(original_assignee)
|
expect(conversation.reload.assignee).to eq(original_assignee)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
describe '#assign_team' do
|
describe '#assign_team' do
|
||||||
|
|||||||
@@ -201,5 +201,21 @@ RSpec.describe AutomationRules::ActionService do
|
|||||||
described_class.new(rule, account, conversation).perform
|
described_class.new(rule, account, conversation).perform
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user