diff --git a/app/javascript/dashboard/composables/spec/useAgentsList.spec.js b/app/javascript/dashboard/composables/spec/useAgentsList.spec.js index 3a39a6be9..33a8edc0d 100644 --- a/app/javascript/dashboard/composables/spec/useAgentsList.spec.js +++ b/app/javascript/dashboard/composables/spec/useAgentsList.spec.js @@ -1,9 +1,9 @@ -import { ref } from 'vue'; -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { useAgentsList } from '../useAgentsList'; import { useMapGetter } from 'dashboard/composables/store'; -import { allAgentsData, formattedAgentsData } from './fixtures/agentFixtures'; import * as agentHelper from 'dashboard/helper/agentHelper'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { ref } from 'vue'; +import { useAgentsList } from '../useAgentsList'; +import { allAgentsData, formattedAgentsData } from './fixtures/agentFixtures'; // Mock vue-i18n vi.mock('vue-i18n', () => ({ @@ -94,6 +94,32 @@ describe('useAgentsList', () => { expect(agentsList.value.length).toBe(formattedAgentsData.slice(1).length); }); + it('keeps nameless agent bots and applies a fallback label', () => { + const namelessBot = { + id: 91, + name: null, + assignee_type: 'AgentBot', + availability_status: 'offline', + }; + mockUseMapGetter({ + 'inboxAssignableAgents/getAssignableAgents': ref(() => [ + ...allAgentsData, + namelessBot, + ]), + }); + + const { agentsList } = useAgentsList(); + // access the computed to trigger evaluation + expect(agentsList.value).toBeDefined(); + + const passedAgents = + agentHelper.getAgentsByUpdatedPresence.mock.calls[0][0]; + expect(passedAgents).toContainEqual({ + ...namelessBot, + name: '-', + }); + }); + it('handles empty assignable agents', () => { mockUseMapGetter({ 'inboxAssignableAgents/getAssignableAgents': ref(() => []), diff --git a/app/javascript/dashboard/composables/useAgentsList.js b/app/javascript/dashboard/composables/useAgentsList.js index 8e8ee5568..d39b54c33 100644 --- a/app/javascript/dashboard/composables/useAgentsList.js +++ b/app/javascript/dashboard/composables/useAgentsList.js @@ -1,10 +1,10 @@ -import { computed } from 'vue'; import { useMapGetter } from 'dashboard/composables/store'; -import { useI18n } from 'vue-i18n'; import { getAgentsByUpdatedPresence, getSortedAgentsByAvailability, } from 'dashboard/helper/agentHelper'; +import { computed } from 'vue'; +import { useI18n } from 'vue-i18n'; /** * A composable function that provides a list of agents for assignment. @@ -53,7 +53,11 @@ export function useAgentsList( * @type {import('vue').ComputedRef} */ const agentsList = computed(() => { - const agents = assignableAgents.value || []; + const agents = (assignableAgents.value || []).map(agent => + !agent.name && agent.assignee_type === 'AgentBot' + ? { ...agent, name: '-' } + : agent + ); const agentsByUpdatedPresence = getAgentsByUpdatedPresence( agents, currentUser.value, diff --git a/app/javascript/dashboard/helper/agentHelper.js b/app/javascript/dashboard/helper/agentHelper.js index ff1123f66..6d592e551 100644 --- a/app/javascript/dashboard/helper/agentHelper.js +++ b/app/javascript/dashboard/helper/agentHelper.js @@ -7,7 +7,7 @@ export const getAgentsByAvailability = (agents, availability) => { return agents .filter(agent => agent.availability_status === availability) - .sort((a, b) => a.name.localeCompare(b.name)); + .sort((a, b) => (a.name || '').localeCompare(b.name || '')); }; /** diff --git a/app/javascript/dashboard/helper/specs/agentHelper.spec.js b/app/javascript/dashboard/helper/specs/agentHelper.spec.js index 273834a11..154d8faf4 100644 --- a/app/javascript/dashboard/helper/specs/agentHelper.spec.js +++ b/app/javascript/dashboard/helper/specs/agentHelper.spec.js @@ -26,6 +26,18 @@ describe('agentHelper', () => { offlineAgentsData ); }); + + it('does not throw when an agent has a null name', () => { + const agents = [ + { id: 1, name: null, availability_status: 'offline' }, + { id: 2, name: 'Zoe', availability_status: 'offline' }, + ]; + + expect(() => getAgentsByAvailability(agents, 'offline')).not.toThrow(); + expect( + getAgentsByAvailability(agents, 'offline').map(agent => agent.id) + ).toEqual([1, 2]); + }); }); describe('getSortedAgentsByAvailability', () => { diff --git a/app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue index 88286bd88..5547d7a4a 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/agentBots/Index.vue @@ -135,7 +135,7 @@ onMounted(() => {
props.selectedItem?.assignee_type === 'AgentBot' ); +const selectedItemName = computed(() => + !props.selectedItem?.name && isAgentBot.value ? '-' : props.selectedItem?.name +); + const selectedThumbnail = computed( () => props.selectedItem?.thumbnail || props.selectedItem?.avatar_url ); @@ -95,16 +99,16 @@ const selectedThumbnail = computed(

- {{ selectedItem.name }} + {{ selectedItemName }}

{ - return option.name.toLowerCase().includes(this.search.toLowerCase()); + return (option.name || '') + .toLowerCase() + .includes(this.search.toLowerCase()); }); }, noResult() {