From 6e46be36c82d1321313451efb41c21fbbce57a59 Mon Sep 17 00:00:00 2001 From: Petterson <58094725+hahuma@users.noreply.github.com> Date: Wed, 11 Mar 2026 06:01:53 -0300 Subject: [PATCH] fix: Add fix to only allow confirmed agents to used in Agent Assingments at Macros/Automations (#13225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Pull Request Template ## Description Unconfirmed agents (pending email verification) were incorrectly appearing in the "assign agent" dropdown for macros and automations. This fix filters out unconfirmed agents from these dropdowns and adds backend validation to prevent assignment of unconfirmed agents. Fixes #13223 ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? **Backend tests:** ```bash docker compose run --rm rails bundle exec rspec spec/services/action_service_spec.rb ``` - Added tests for confirmed agent assignment (should succeed) - Added tests for unconfirmed agent assignment (should be skipped) **Frontend tests:** ```bash docker compose run --rm rails pnpm test app/javascript/dashboard/composables/spec/useMacros.spec.js ``` - Updated mocks to use `getVerifiedAgents` getter **Manual testing:** 1. Create an unconfirmed agent via platform 2. Navigate to Settings → Macros → New Macro → Add "Assign Agent" action 3. Verify unconfirmed agent does NOT appear in dropdown 4. Navigate to Settings → Automations → New Automation → Add "Assign Agent" action 5. Verify unconfirmed agent does NOT appear in dropdown ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sojan Jose --- .../composables/spec/useAutomation.spec.js | 2 +- .../composables/spec/useMacros.spec.js | 4 ++-- .../composables/useAutomationValues.js | 2 +- .../dashboard/composables/useMacros.js | 2 +- app/services/action_service.rb | 4 +++- spec/services/action_service_spec.rb | 20 +++++++++++++++++++ 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/javascript/dashboard/composables/spec/useAutomation.spec.js b/app/javascript/dashboard/composables/spec/useAutomation.spec.js index a66f0c7b4..6cce15996 100644 --- a/app/javascript/dashboard/composables/spec/useAutomation.spec.js +++ b/app/javascript/dashboard/composables/spec/useAutomation.spec.js @@ -38,7 +38,7 @@ describe('useAutomation', () => { }); useMapGetter.mockImplementation(getter => { const getterMap = { - 'agents/getAgents': agents, + 'agents/getVerifiedAgents': agents, 'campaigns/getAllCampaigns': campaigns, 'contacts/getContacts': contacts, 'inboxes/getInboxes': inboxes, diff --git a/app/javascript/dashboard/composables/spec/useMacros.spec.js b/app/javascript/dashboard/composables/spec/useMacros.spec.js index 7666e4afa..e7f6aa495 100644 --- a/app/javascript/dashboard/composables/spec/useMacros.spec.js +++ b/app/javascript/dashboard/composables/spec/useMacros.spec.js @@ -111,7 +111,7 @@ describe('useMacros', () => { useStoreGetters.mockReturnValue({ 'labels/getLabels': { value: mockLabels }, 'teams/getTeams': { value: mockTeams }, - 'agents/getAgents': { value: mockAgents }, + 'agents/getVerifiedAgents': { value: mockAgents }, }); }); @@ -167,7 +167,7 @@ describe('useMacros', () => { useStoreGetters.mockReturnValue({ 'labels/getLabels': { value: [] }, 'teams/getTeams': { value: [] }, - 'agents/getAgents': { value: [] }, + 'agents/getVerifiedAgents': { value: [] }, }); const { getMacroDropdownValues } = useMacros(); diff --git a/app/javascript/dashboard/composables/useAutomationValues.js b/app/javascript/dashboard/composables/useAutomationValues.js index 6a6b507c8..709aba20b 100644 --- a/app/javascript/dashboard/composables/useAutomationValues.js +++ b/app/javascript/dashboard/composables/useAutomationValues.js @@ -20,7 +20,7 @@ import { export default function useAutomationValues() { const getters = useStoreGetters(); const { t } = useI18n(); - const agents = useMapGetter('agents/getAgents'); + const agents = useMapGetter('agents/getVerifiedAgents'); const campaigns = useMapGetter('campaigns/getAllCampaigns'); const contacts = useMapGetter('contacts/getContacts'); const inboxes = useMapGetter('inboxes/getInboxes'); diff --git a/app/javascript/dashboard/composables/useMacros.js b/app/javascript/dashboard/composables/useMacros.js index 8f0227fa3..437690eff 100644 --- a/app/javascript/dashboard/composables/useMacros.js +++ b/app/javascript/dashboard/composables/useMacros.js @@ -13,7 +13,7 @@ export const useMacros = () => { const labels = computed(() => getters['labels/getLabels'].value); const teams = computed(() => getters['teams/getTeams'].value); - const agents = computed(() => getters['agents/getAgents'].value); + const agents = computed(() => getters['agents/getVerifiedAgents'].value); /** * Get dropdown values based on the specified type diff --git a/app/services/action_service.rb b/app/services/action_service.rb index f6972cd35..86e2810cf 100644 --- a/app/services/action_service.rb +++ b/app/services/action_service.rb @@ -47,7 +47,9 @@ class ActionService @agent = @account.users.find_by(id: agent_ids) - @conversation.update!(assignee_id: @agent.id) if @agent.present? + return unless @agent.present? && @agent.confirmed? + + @conversation.update!(assignee_id: @agent.id) end def remove_label(labels) diff --git a/spec/services/action_service_spec.rb b/spec/services/action_service_spec.rb index 95b1e6ecf..f4c20b191 100644 --- a/spec/services/action_service_spec.rb +++ b/spec/services/action_service_spec.rb @@ -50,6 +50,26 @@ describe ActionService do action_service.assign_agent(['nil']) expect(conversation.reload.assignee).to be_nil end + + context 'when agent is confirmed' do + it 'assigns the agent to the conversation' do + inbox_member + action_service.assign_agent([agent.id]) + expect(conversation.reload.assignee).to eq(agent) + end + end + + context 'when agent is unconfirmed' do + let(:unconfirmed_agent) { create(:user, account: account, role: :agent, skip_confirmation: false) } + let(:unconfirmed_inbox_member) { create(:inbox_member, inbox: conversation.inbox, user: unconfirmed_agent) } + + it 'does not assign unconfirmed agent to the conversation' do + unconfirmed_inbox_member + original_assignee = conversation.assignee + action_service.assign_agent([unconfirmed_agent.id]) + expect(conversation.reload.assignee).to eq(original_assignee) + end + end end describe '#assign_team' do