feat: Sort agents on availability status (#7174)
This commit is contained in:
@@ -79,6 +79,7 @@ import MenuItem from './menuItem.vue';
|
|||||||
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
|
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
|
||||||
import wootConstants from 'dashboard/constants/globals';
|
import wootConstants from 'dashboard/constants/globals';
|
||||||
import snoozeTimesMixin from 'dashboard/mixins/conversation/snoozeTimesMixin';
|
import snoozeTimesMixin from 'dashboard/mixins/conversation/snoozeTimesMixin';
|
||||||
|
import agentMixin from 'dashboard/mixins/agentMixin';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import AgentLoadingPlaceholder from './agentLoadingPlaceholder.vue';
|
import AgentLoadingPlaceholder from './agentLoadingPlaceholder.vue';
|
||||||
export default {
|
export default {
|
||||||
@@ -87,7 +88,7 @@ export default {
|
|||||||
MenuItemWithSubmenu,
|
MenuItemWithSubmenu,
|
||||||
AgentLoadingPlaceholder,
|
AgentLoadingPlaceholder,
|
||||||
},
|
},
|
||||||
mixins: [snoozeTimesMixin],
|
mixins: [snoozeTimesMixin, agentMixin],
|
||||||
props: {
|
props: {
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -202,6 +203,16 @@ export default {
|
|||||||
teams: 'teams/getTeams',
|
teams: 'teams/getTeams',
|
||||||
assignableAgentsUiFlags: 'inboxAssignableAgents/getUIFlags',
|
assignableAgentsUiFlags: 'inboxAssignableAgents/getUIFlags',
|
||||||
}),
|
}),
|
||||||
|
filteredAgentOnAvailability() {
|
||||||
|
const agents = this.$store.getters[
|
||||||
|
'inboxAssignableAgents/getAssignableAgents'
|
||||||
|
](this.inboxId);
|
||||||
|
const agentsByUpdatedPresence = this.getAgentsByUpdatedPresence(agents);
|
||||||
|
const filteredAgents = this.sortedAgentsByAvailability(
|
||||||
|
agentsByUpdatedPresence
|
||||||
|
);
|
||||||
|
return filteredAgents;
|
||||||
|
},
|
||||||
assignableAgents() {
|
assignableAgents() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -212,9 +223,7 @@ export default {
|
|||||||
account_id: 0,
|
account_id: 0,
|
||||||
email: 'None',
|
email: 'None',
|
||||||
},
|
},
|
||||||
...this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
...this.filteredAgentOnAvailability,
|
||||||
this.inboxId
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -246,6 +255,7 @@ export default {
|
|||||||
...(type === 'icon' && { icon: option.icon }),
|
...(type === 'icon' && { icon: option.icon }),
|
||||||
...(type === 'label' && { color: option.color }),
|
...(type === 'label' && { color: option.color }),
|
||||||
...(type === 'agent' && { thumbnail: option.thumbnail }),
|
...(type === 'agent' && { thumbnail: option.thumbnail }),
|
||||||
|
...(type === 'agent' && { status: option.availability_status }),
|
||||||
...(type === 'text' && { label: option.label }),
|
...(type === 'text' && { label: option.label }),
|
||||||
...(type === 'label' && { label: option.title }),
|
...(type === 'label' && { label: option.title }),
|
||||||
...(type === 'agent' && { label: option.name }),
|
...(type === 'agent' && { label: option.name }),
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
v-if="variant === 'agent'"
|
v-if="variant === 'agent'"
|
||||||
:username="option.label"
|
:username="option.label"
|
||||||
:src="option.thumbnail"
|
:src="option.thumbnail"
|
||||||
|
:status="option.status"
|
||||||
size="20px"
|
size="20px"
|
||||||
class="agent-thumbnail"
|
class="agent-thumbnail"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,39 +2,68 @@ import { mapGetters } from 'vuex';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'getCurrentUser',
|
||||||
|
currentAccountId: 'getCurrentAccountId',
|
||||||
|
}),
|
||||||
assignableAgents() {
|
assignableAgents() {
|
||||||
return this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
return this.$store.getters['inboxAssignableAgents/getAssignableAgents'](
|
||||||
this.inboxId
|
this.inboxId
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
...mapGetters({ currentUser: 'getCurrentUser' }),
|
|
||||||
isAgentSelected() {
|
isAgentSelected() {
|
||||||
return this.currentChat?.meta?.assignee;
|
return this.currentChat?.meta?.assignee;
|
||||||
},
|
},
|
||||||
|
createNoneAgent() {
|
||||||
|
return {
|
||||||
|
confirmed: true,
|
||||||
|
name: 'None',
|
||||||
|
id: 0,
|
||||||
|
role: 'agent',
|
||||||
|
account_id: 0,
|
||||||
|
email: 'None',
|
||||||
|
};
|
||||||
|
},
|
||||||
agentsList() {
|
agentsList() {
|
||||||
const agents = this.assignableAgents || [];
|
const agents = this.assignableAgents || [];
|
||||||
return [
|
const agentsByUpdatedPresence = this.getAgentsByUpdatedPresence(agents);
|
||||||
...(this.isAgentSelected
|
const none = this.createNoneAgent;
|
||||||
? [
|
const filteredAgentsByAvailability = this.sortedAgentsByAvailability(
|
||||||
{
|
agentsByUpdatedPresence
|
||||||
confirmed: true,
|
);
|
||||||
name: 'None',
|
const filteredAgents = [
|
||||||
id: 0,
|
...(this.isAgentSelected ? [none] : []),
|
||||||
role: 'agent',
|
...filteredAgentsByAvailability,
|
||||||
account_id: 0,
|
];
|
||||||
email: 'None',
|
return filteredAgents;
|
||||||
},
|
},
|
||||||
]
|
},
|
||||||
: []),
|
methods: {
|
||||||
...agents,
|
getAgentsByAvailability(agents, availability) {
|
||||||
].map(item =>
|
return agents
|
||||||
|
.filter(agent => agent.availability_status === availability)
|
||||||
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
},
|
||||||
|
sortedAgentsByAvailability(agents) {
|
||||||
|
const onlineAgents = this.getAgentsByAvailability(agents, 'online');
|
||||||
|
const busyAgents = this.getAgentsByAvailability(agents, 'busy');
|
||||||
|
const offlineAgents = this.getAgentsByAvailability(agents, 'offline');
|
||||||
|
const filteredAgents = [...onlineAgents, ...busyAgents, ...offlineAgents];
|
||||||
|
return filteredAgents;
|
||||||
|
},
|
||||||
|
getAgentsByUpdatedPresence(agents) {
|
||||||
|
// Here we are updating the availability status of the current user dynamically (live) based on the current account availability status
|
||||||
|
const agentsWithDynamicPresenceUpdate = agents.map(item =>
|
||||||
item.id === this.currentUser.id
|
item.id === this.currentUser.id
|
||||||
? {
|
? {
|
||||||
...item,
|
...item,
|
||||||
availability_status: this.currentUser.availability_status,
|
availability_status: this.currentUser.accounts.find(
|
||||||
|
account => account.id === this.currentAccountId
|
||||||
|
).availability_status,
|
||||||
}
|
}
|
||||||
: item
|
: item
|
||||||
);
|
);
|
||||||
|
return agentsWithDynamicPresenceUpdate;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,36 @@ export default {
|
|||||||
name: 'Samuel Keta',
|
name: 'Samuel Keta',
|
||||||
role: 'agent',
|
role: 'agent',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'offline',
|
||||||
|
available_name: 'James K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'james@chatwoot.com',
|
||||||
|
id: 3,
|
||||||
|
name: 'James Koti',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'busy',
|
||||||
|
available_name: 'Honey',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'bee@chatwoot.com',
|
||||||
|
id: 4,
|
||||||
|
name: 'Honey Bee',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
available_name: 'Abraham',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'abraham@chatwoot.com',
|
||||||
|
id: 5,
|
||||||
|
name: 'Abraham Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
formattedAgents: [
|
formattedAgents: [
|
||||||
{
|
{
|
||||||
@@ -32,7 +62,17 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
account_id: 1,
|
account_id: 1,
|
||||||
availability_status: 'busy',
|
availability_status: 'online',
|
||||||
|
available_name: 'Abraham',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'abraham@chatwoot.com',
|
||||||
|
id: 5,
|
||||||
|
name: 'Abraham Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
available_name: 'John K',
|
available_name: 'John K',
|
||||||
confirmed: true,
|
confirmed: true,
|
||||||
email: 'john@chatwoot.com',
|
email: 'john@chatwoot.com',
|
||||||
@@ -40,6 +80,70 @@ export default {
|
|||||||
name: 'John Kennady',
|
name: 'John Kennady',
|
||||||
role: 'administrator',
|
role: 'administrator',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'busy',
|
||||||
|
available_name: 'Honey',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'bee@chatwoot.com',
|
||||||
|
id: 4,
|
||||||
|
name: 'Honey Bee',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'busy',
|
||||||
|
available_name: 'Samuel K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'samuel@chatwoot.com',
|
||||||
|
id: 2,
|
||||||
|
name: 'Samuel Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'offline',
|
||||||
|
available_name: 'James K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'james@chatwoot.com',
|
||||||
|
id: 3,
|
||||||
|
name: 'James Koti',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onlineAgents: [
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
available_name: 'Abraham',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'abraham@chatwoot.com',
|
||||||
|
id: 5,
|
||||||
|
name: 'Abraham Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
available_name: 'John K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'john@chatwoot.com',
|
||||||
|
id: 1,
|
||||||
|
name: 'John Kennady',
|
||||||
|
role: 'administrator',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
busyAgents: [
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'busy',
|
||||||
|
available_name: 'Honey',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'bee@chatwoot.com',
|
||||||
|
id: 4,
|
||||||
|
name: 'Honey Bee',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
account_id: 1,
|
account_id: 1,
|
||||||
availability_status: 'busy',
|
availability_status: 'busy',
|
||||||
@@ -51,4 +155,92 @@ export default {
|
|||||||
role: 'agent',
|
role: 'agent',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
offlineAgents: [
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'offline',
|
||||||
|
available_name: 'James K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'james@chatwoot.com',
|
||||||
|
id: 3,
|
||||||
|
name: 'James Koti',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortedByAvailability: [
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
available_name: 'Abraham',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'abraham@chatwoot.com',
|
||||||
|
id: 5,
|
||||||
|
name: 'Abraham Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
available_name: 'John K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'john@chatwoot.com',
|
||||||
|
id: 1,
|
||||||
|
name: 'John Kennady',
|
||||||
|
role: 'administrator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'busy',
|
||||||
|
available_name: 'Honey',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'bee@chatwoot.com',
|
||||||
|
id: 4,
|
||||||
|
name: 'Honey Bee',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'busy',
|
||||||
|
available_name: 'Samuel K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'samuel@chatwoot.com',
|
||||||
|
id: 2,
|
||||||
|
name: 'Samuel Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'offline',
|
||||||
|
available_name: 'James K',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'james@chatwoot.com',
|
||||||
|
id: 3,
|
||||||
|
name: 'James Koti',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
formattedAgentsByPresenceOnline: [
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
available_name: 'Abraham',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'abr@chatwoot.com',
|
||||||
|
id: 1,
|
||||||
|
name: 'Abraham Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
formattedAgentsByPresenceOffline: [
|
||||||
|
{
|
||||||
|
account_id: 1,
|
||||||
|
availability_status: 'offline',
|
||||||
|
available_name: 'Abraham',
|
||||||
|
confirmed: true,
|
||||||
|
email: 'abr@chatwoot.com',
|
||||||
|
id: 1,
|
||||||
|
name: 'Abraham Keta',
|
||||||
|
role: 'agent',
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,12 +12,71 @@ describe('agentMixin', () => {
|
|||||||
getters = {
|
getters = {
|
||||||
getCurrentUser: () => ({
|
getCurrentUser: () => ({
|
||||||
id: 1,
|
id: 1,
|
||||||
availability_status: 'busy',
|
accounts: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
availability_status: 'online',
|
||||||
|
auto_offline: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
|
getCurrentAccountId: () => 1,
|
||||||
};
|
};
|
||||||
store = new Vuex.Store({ getters });
|
store = new Vuex.Store({ getters });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('return agents by availability', () => {
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
title: 'TestComponent',
|
||||||
|
mixins: [agentMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inboxId: 1,
|
||||||
|
currentChat: { meta: { assignee: { name: 'John' } } },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
assignableAgents() {
|
||||||
|
return agentFixtures.allAgents;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(Component, { store, localVue });
|
||||||
|
expect(
|
||||||
|
wrapper.vm.getAgentsByAvailability(agentFixtures.allAgents, 'online')
|
||||||
|
).toEqual(agentFixtures.onlineAgents);
|
||||||
|
expect(
|
||||||
|
wrapper.vm.getAgentsByAvailability(agentFixtures.allAgents, 'busy')
|
||||||
|
).toEqual(agentFixtures.busyAgents);
|
||||||
|
expect(
|
||||||
|
wrapper.vm.getAgentsByAvailability(agentFixtures.allAgents, 'offline')
|
||||||
|
).toEqual(agentFixtures.offlineAgents);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return sorted agents by availability', () => {
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
title: 'TestComponent',
|
||||||
|
mixins: [agentMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inboxId: 1,
|
||||||
|
currentChat: { meta: { assignee: { name: 'John' } } },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
assignableAgents() {
|
||||||
|
return agentFixtures.allAgents;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(Component, { store, localVue });
|
||||||
|
expect(
|
||||||
|
wrapper.vm.sortedAgentsByAvailability(agentFixtures.allAgents)
|
||||||
|
).toEqual(agentFixtures.sortedByAvailability);
|
||||||
|
});
|
||||||
|
|
||||||
it('return formatted agents', () => {
|
it('return formatted agents', () => {
|
||||||
const Component = {
|
const Component = {
|
||||||
render() {},
|
render() {},
|
||||||
@@ -38,4 +97,44 @@ describe('agentMixin', () => {
|
|||||||
const wrapper = shallowMount(Component, { store, localVue });
|
const wrapper = shallowMount(Component, { store, localVue });
|
||||||
expect(wrapper.vm.agentsList).toEqual(agentFixtures.formattedAgents);
|
expect(wrapper.vm.agentsList).toEqual(agentFixtures.formattedAgents);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('return formatted agents by presence', () => {
|
||||||
|
const Component = {
|
||||||
|
render() {},
|
||||||
|
title: 'TestComponent',
|
||||||
|
mixins: [agentMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inboxId: 1,
|
||||||
|
currentChat: { meta: { assignee: { name: 'John' } } },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentUser() {
|
||||||
|
return {
|
||||||
|
id: 1,
|
||||||
|
accounts: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
availability_status: 'offline',
|
||||||
|
auto_offline: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
currentAccountId() {
|
||||||
|
return 1;
|
||||||
|
},
|
||||||
|
assignableAgents() {
|
||||||
|
return agentFixtures.allAgents;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(Component, { store, localVue });
|
||||||
|
expect(
|
||||||
|
wrapper.vm.getAgentsByUpdatedPresence(
|
||||||
|
agentFixtures.formattedAgentsByPresenceOnline
|
||||||
|
)
|
||||||
|
).toEqual(agentFixtures.formattedAgentsByPresenceOffline);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user