Merge branch 'develop' into chore/conversation-participants
This commit is contained in:
commit
98d6413ee8
25 changed files with 276 additions and 69 deletions
|
@ -56,7 +56,6 @@ class ConversationFinder
|
||||||
filter_by_team if @team
|
filter_by_team if @team
|
||||||
filter_by_labels if params[:labels]
|
filter_by_labels if params[:labels]
|
||||||
filter_by_query if params[:q]
|
filter_by_query if params[:q]
|
||||||
filter_by_reply_status
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_inboxes
|
def set_inboxes
|
||||||
|
@ -76,15 +75,9 @@ class ConversationFinder
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_all_conversations
|
def find_all_conversations
|
||||||
case params[:conversation_type]
|
|
||||||
when 'mention'
|
|
||||||
conversation_ids = current_account.mentions.where(user: current_user).pluck(:conversation_id)
|
|
||||||
@conversations = current_account.conversations.where(id: conversation_ids)
|
|
||||||
when 'participating'
|
|
||||||
@conversations = current_user.participating_conversations.where(account_id: current_account.id)
|
|
||||||
else
|
|
||||||
@conversations = current_account.conversations.where(inbox_id: @inbox_ids)
|
@conversations = current_account.conversations.where(inbox_id: @inbox_ids)
|
||||||
end
|
filter_by_conversation_type if params[:conversation_type]
|
||||||
|
@conversations
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_assignee_type
|
def filter_by_assignee_type
|
||||||
|
@ -99,8 +92,17 @@ class ConversationFinder
|
||||||
@conversations
|
@conversations
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_reply_status
|
def filter_by_conversation_type
|
||||||
@conversations = @conversations.where(first_reply_created_at: nil) if params[:reply_status] == 'unattended'
|
case @params[:conversation_type]
|
||||||
|
when 'mention'
|
||||||
|
conversation_ids = current_account.mentions.where(user: current_user).pluck(:conversation_id)
|
||||||
|
@conversations = @conversations.where(id: conversation_ids)
|
||||||
|
when 'participating'
|
||||||
|
@conversations = current_user.participating_conversations.where(account_id: current_account.id)
|
||||||
|
when 'unattended'
|
||||||
|
@conversations = @conversations.where(first_reply_created_at: nil)
|
||||||
|
end
|
||||||
|
@conversations
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_by_query
|
def filter_by_query
|
||||||
|
|
|
@ -335,10 +335,8 @@ export default {
|
||||||
status: this.activeStatus,
|
status: this.activeStatus,
|
||||||
page: this.currentPage + 1,
|
page: this.currentPage + 1,
|
||||||
labels: this.label ? [this.label] : undefined,
|
labels: this.label ? [this.label] : undefined,
|
||||||
teamId: this.teamId ? this.teamId : undefined,
|
teamId: this.teamId || undefined,
|
||||||
conversationType: this.conversationType
|
conversationType: this.conversationType || undefined,
|
||||||
? this.conversationType
|
|
||||||
: undefined,
|
|
||||||
folders: this.hasActiveFolders ? this.savedFoldersValue : undefined,
|
folders: this.hasActiveFolders ? this.savedFoldersValue : undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -358,6 +356,9 @@ export default {
|
||||||
if (this.conversationType === 'participating') {
|
if (this.conversationType === 'participating') {
|
||||||
return this.$t('CONVERSATION_WATCHERS.SIDEBAR_MENU_TITLE');
|
return this.$t('CONVERSATION_WATCHERS.SIDEBAR_MENU_TITLE');
|
||||||
}
|
}
|
||||||
|
if (this.conversationType === 'unattended') {
|
||||||
|
return this.$t('CHAT_LIST.UNATTENDED_HEADING');
|
||||||
|
}
|
||||||
if (this.hasActiveFolders) {
|
if (this.hasActiveFolders) {
|
||||||
return this.activeFolder.name;
|
return this.activeFolder.name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@ const conversations = accountId => ({
|
||||||
'conversation_through_participating',
|
'conversation_through_participating',
|
||||||
'folder_conversations',
|
'folder_conversations',
|
||||||
'conversations_through_folders',
|
'conversations_through_folders',
|
||||||
|
'conversation_unattended',
|
||||||
|
'conversation_through_unattended',
|
||||||
],
|
],
|
||||||
menuItems: [
|
menuItems: [
|
||||||
{
|
{
|
||||||
|
@ -42,6 +44,14 @@ const conversations = accountId => ({
|
||||||
toState: frontendURL(`accounts/${accountId}/participating/conversations`),
|
toState: frontendURL(`accounts/${accountId}/participating/conversations`),
|
||||||
toStateName: 'conversation_participating',
|
toStateName: 'conversation_participating',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'mail-unread',
|
||||||
|
label: 'UNATTENDED_CONVERSATIONS',
|
||||||
|
key: 'conversation_unattended',
|
||||||
|
toState: frontendURL(`accounts/${accountId}/unattended/conversations`),
|
||||||
|
toStateName: 'conversation_unattended',
|
||||||
|
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -450,8 +450,7 @@ export default {
|
||||||
return this.currentChat.id;
|
return this.currentChat.id;
|
||||||
},
|
},
|
||||||
conversationIdByRoute() {
|
conversationIdByRoute() {
|
||||||
const { conversation_id: conversationId } = this.$route.params;
|
return this.conversationId;
|
||||||
return conversationId;
|
|
||||||
},
|
},
|
||||||
editorStateId() {
|
editorStateId() {
|
||||||
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
return `draft-${this.conversationIdByRoute}-${this.replyType}`;
|
||||||
|
|
|
@ -58,6 +58,8 @@ export const conversationUrl = ({
|
||||||
url = `accounts/${accountId}/mentions/conversations/${id}`;
|
url = `accounts/${accountId}/mentions/conversations/${id}`;
|
||||||
} else if (conversationType === 'participating') {
|
} else if (conversationType === 'participating') {
|
||||||
url = `accounts/${accountId}/participating/conversations/${id}`;
|
url = `accounts/${accountId}/participating/conversations/${id}`;
|
||||||
|
} else if (conversationType === 'unattended') {
|
||||||
|
url = `accounts/${accountId}/unattended/conversations/${id}`;
|
||||||
}
|
}
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
},
|
},
|
||||||
"TAB_HEADING": "Conversations",
|
"TAB_HEADING": "Conversations",
|
||||||
"MENTION_HEADING": "Mentions",
|
"MENTION_HEADING": "Mentions",
|
||||||
|
"UNATTENDED_HEADING": "Unattended",
|
||||||
"SEARCH": {
|
"SEARCH": {
|
||||||
"INPUT": "Search for People, Chats, Saved Replies .."
|
"INPUT": "Search for People, Chats, Saved Replies .."
|
||||||
},
|
},
|
||||||
|
|
|
@ -178,6 +178,7 @@
|
||||||
"ALL_CONVERSATIONS": "All Conversations",
|
"ALL_CONVERSATIONS": "All Conversations",
|
||||||
"MENTIONED_CONVERSATIONS": "Mentions",
|
"MENTIONED_CONVERSATIONS": "Mentions",
|
||||||
"PARTICIPATING_CONVERSATIONS": "Participating",
|
"PARTICIPATING_CONVERSATIONS": "Participating",
|
||||||
|
"UNATTENDED_CONVERSATIONS": "Unattended",
|
||||||
"REPORTS": "Reports",
|
"REPORTS": "Reports",
|
||||||
"SETTINGS": "Settings",
|
"SETTINGS": "Settings",
|
||||||
"CONTACTS": "Contacts",
|
"CONTACTS": "Contacts",
|
||||||
|
|
|
@ -133,11 +133,24 @@ export default {
|
||||||
'accounts/:accountId/participating/conversations/:conversationId'
|
'accounts/:accountId/participating/conversations/:conversationId'
|
||||||
),
|
),
|
||||||
name: 'conversation_through_participating',
|
name: 'conversation_through_participating',
|
||||||
|
|
||||||
|
{
|
||||||
|
path: frontendURL('accounts/:accountId/unattended/conversations'),
|
||||||
|
name: 'conversation_unattended',
|
||||||
|
roles: ['administrator', 'agent'],
|
||||||
|
component: ConversationView,
|
||||||
|
props: () => ({ conversationType: 'unattended' }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: frontendURL(
|
||||||
|
'accounts/:accountId/unattended/conversations/:conversationId'
|
||||||
|
),
|
||||||
|
name: 'conversation_through_unattended',
|
||||||
roles: ['administrator', 'agent'],
|
roles: ['administrator', 'agent'],
|
||||||
component: ConversationView,
|
component: ConversationView,
|
||||||
props: route => ({
|
props: route => ({
|
||||||
conversationId: route.params.conversationId,
|
conversationId: route.params.conversationId,
|
||||||
conversationType: 'participating',
|
conversationType: 'unattended',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { createPendingMessage } from 'dashboard/helper/commons';
|
||||||
import {
|
import {
|
||||||
buildConversationList,
|
buildConversationList,
|
||||||
isOnMentionsView,
|
isOnMentionsView,
|
||||||
|
isOnUnattendedView,
|
||||||
} from './helpers/actionHelpers';
|
} from './helpers/actionHelpers';
|
||||||
import messageReadActions from './actions/messageReadActions';
|
import messageReadActions from './actions/messageReadActions';
|
||||||
// actions
|
// actions
|
||||||
|
@ -230,6 +231,7 @@ const actions = {
|
||||||
if (
|
if (
|
||||||
!hasAppliedFilters &&
|
!hasAppliedFilters &&
|
||||||
!isOnMentionsView(rootState) &&
|
!isOnMentionsView(rootState) &&
|
||||||
|
!isOnUnattendedView(rootState) &&
|
||||||
isMatchingInboxFilter
|
isMatchingInboxFilter
|
||||||
) {
|
) {
|
||||||
commit(types.ADD_CONVERSATION, conversation);
|
commit(types.ADD_CONVERSATION, conversation);
|
||||||
|
@ -243,6 +245,12 @@ const actions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addUnattended({ dispatch, rootState }, conversation) {
|
||||||
|
if (isOnUnattendedView(rootState)) {
|
||||||
|
dispatch('updateConversation', conversation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
updateConversation({ commit, dispatch }, conversation) {
|
updateConversation({ commit, dispatch }, conversation) {
|
||||||
const {
|
const {
|
||||||
meta: { sender },
|
meta: { sender },
|
||||||
|
|
|
@ -5,33 +5,54 @@ export const findPendingMessageIndex = (chat, message) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterByStatus = (chatStatus, filterStatus) =>
|
export const filterByStatus = (chatStatus, filterStatus) =>
|
||||||
filterStatus === 'all' ? true : chatStatus === filterStatus;
|
filterStatus === 'all' ? true : chatStatus === filterStatus;
|
||||||
|
|
||||||
|
export const filterByInbox = (shouldFilter, inboxId, chatInboxId) => {
|
||||||
|
const isOnInbox = Number(inboxId) === chatInboxId;
|
||||||
|
return inboxId ? isOnInbox && shouldFilter : shouldFilter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterByTeam = (shouldFilter, teamId, chatTeamId) => {
|
||||||
|
const isOnTeam = Number(teamId) === chatTeamId;
|
||||||
|
return teamId ? isOnTeam && shouldFilter : shouldFilter;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const filterByLabel = (shouldFilter, labels, chatLabels) => {
|
||||||
|
const isOnLabel = labels.every(label => chatLabels.includes(label));
|
||||||
|
return labels.length ? isOnLabel && shouldFilter : shouldFilter;
|
||||||
|
};
|
||||||
|
export const filterByUnattended = (
|
||||||
|
shouldFilter,
|
||||||
|
conversationType,
|
||||||
|
firstReplyOn
|
||||||
|
) => {
|
||||||
|
return conversationType === 'unattended'
|
||||||
|
? !firstReplyOn && shouldFilter
|
||||||
|
: shouldFilter;
|
||||||
|
};
|
||||||
|
|
||||||
export const applyPageFilters = (conversation, filters) => {
|
export const applyPageFilters = (conversation, filters) => {
|
||||||
const { inboxId, status, labels = [], teamId } = filters;
|
const { inboxId, status, labels = [], teamId, conversationType } = filters;
|
||||||
const {
|
const {
|
||||||
status: chatStatus,
|
status: chatStatus,
|
||||||
inbox_id: chatInboxId,
|
inbox_id: chatInboxId,
|
||||||
labels: chatLabels = [],
|
labels: chatLabels = [],
|
||||||
meta = {},
|
meta = {},
|
||||||
|
first_reply_created_at: firstReplyOn,
|
||||||
} = conversation;
|
} = conversation;
|
||||||
const team = meta.team || {};
|
const team = meta.team || {};
|
||||||
const { id: chatTeamId } = team;
|
const { id: chatTeamId } = team;
|
||||||
|
|
||||||
let shouldFilter = filterByStatus(chatStatus, status);
|
let shouldFilter = filterByStatus(chatStatus, status);
|
||||||
if (inboxId) {
|
shouldFilter = filterByInbox(shouldFilter, inboxId, chatInboxId);
|
||||||
const filterByInbox = Number(inboxId) === chatInboxId;
|
shouldFilter = filterByTeam(shouldFilter, teamId, chatTeamId);
|
||||||
shouldFilter = shouldFilter && filterByInbox;
|
shouldFilter = filterByLabel(shouldFilter, labels, chatLabels);
|
||||||
}
|
shouldFilter = filterByUnattended(
|
||||||
if (teamId) {
|
shouldFilter,
|
||||||
const filterByTeam = Number(teamId) === chatTeamId;
|
conversationType,
|
||||||
shouldFilter = shouldFilter && filterByTeam;
|
firstReplyOn
|
||||||
}
|
);
|
||||||
if (labels.length) {
|
|
||||||
const filterByLabels = labels.every(label => chatLabels.includes(label));
|
|
||||||
shouldFilter = shouldFilter && filterByLabels;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shouldFilter;
|
return shouldFilter;
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,14 @@ export const isOnMentionsView = ({ route: { name: routeName } }) => {
|
||||||
return MENTION_ROUTES.includes(routeName);
|
return MENTION_ROUTES.includes(routeName);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isOnUnattendedView = ({ route: { name: routeName } }) => {
|
||||||
|
const UNATTENDED_ROUTES = [
|
||||||
|
'conversation_unattended',
|
||||||
|
'conversation_through_unattended',
|
||||||
|
];
|
||||||
|
return UNATTENDED_ROUTES.includes(routeName);
|
||||||
|
};
|
||||||
|
|
||||||
export const buildConversationList = (
|
export const buildConversationList = (
|
||||||
context,
|
context,
|
||||||
requestPayload,
|
requestPayload,
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import {
|
import {
|
||||||
findPendingMessageIndex,
|
findPendingMessageIndex,
|
||||||
applyPageFilters,
|
applyPageFilters,
|
||||||
|
filterByInbox,
|
||||||
|
filterByTeam,
|
||||||
|
filterByLabel,
|
||||||
|
filterByUnattended,
|
||||||
} from '../../conversations/helpers';
|
} from '../../conversations/helpers';
|
||||||
|
|
||||||
const conversationList = [
|
const conversationList = [
|
||||||
|
@ -119,3 +123,52 @@ describe('#applyPageFilters', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#filterByInbox', () => {
|
||||||
|
it('returns true if conversation has inbox filter active', () => {
|
||||||
|
const inboxId = '1';
|
||||||
|
const chatInboxId = 1;
|
||||||
|
expect(filterByInbox(true, inboxId, chatInboxId)).toEqual(true);
|
||||||
|
});
|
||||||
|
it('returns false if inbox filter is not active', () => {
|
||||||
|
const inboxId = '1';
|
||||||
|
const chatInboxId = 13;
|
||||||
|
expect(filterByInbox(true, inboxId, chatInboxId)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#filterByTeam', () => {
|
||||||
|
it('returns true if conversation has team and team filter is active', () => {
|
||||||
|
const [teamId, chatTeamId] = ['1', 1];
|
||||||
|
expect(filterByTeam(true, teamId, chatTeamId)).toEqual(true);
|
||||||
|
});
|
||||||
|
it('returns false if team filter is not active', () => {
|
||||||
|
const [teamId, chatTeamId] = ['1', 12];
|
||||||
|
expect(filterByTeam(true, teamId, chatTeamId)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#filterByLabel', () => {
|
||||||
|
it('returns true if conversation has labels and labels filter is active', () => {
|
||||||
|
const labels = ['dev', 'cs'];
|
||||||
|
const chatLabels = ['dev', 'cs', 'sales'];
|
||||||
|
expect(filterByLabel(true, labels, chatLabels)).toEqual(true);
|
||||||
|
});
|
||||||
|
it('returns false if conversation has not all labels', () => {
|
||||||
|
const labels = ['dev', 'cs', 'sales'];
|
||||||
|
const chatLabels = ['cs', 'sales'];
|
||||||
|
expect(filterByLabel(true, labels, chatLabels)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#filterByUnattended', () => {
|
||||||
|
it('returns true if conversation type is unattended and has no first reply', () => {
|
||||||
|
expect(filterByUnattended(true, 'unattended', undefined)).toEqual(true);
|
||||||
|
});
|
||||||
|
it('returns false if conversation type is not unattended and has no first reply', () => {
|
||||||
|
expect(filterByUnattended(false, 'mentions', undefined)).toEqual(false);
|
||||||
|
});
|
||||||
|
it('returns true if conversation type is unattended and has first reply', () => {
|
||||||
|
expect(filterByUnattended(true, 'mentions', 123)).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
"location-outline": "M5.843 4.568a8.707 8.707 0 1 1 12.314 12.314l-1.187 1.174c-.875.858-2.01 1.962-3.406 3.312a2.25 2.25 0 0 1-3.128 0l-3.491-3.396c-.439-.431-.806-.794-1.102-1.09a8.707 8.707 0 0 1 0-12.314Zm11.253 1.06A7.207 7.207 0 1 0 6.904 15.822L8.39 17.29a753.98 753.98 0 0 0 3.088 3 .75.75 0 0 0 1.043 0l3.394-3.3c.47-.461.863-.85 1.18-1.168a7.207 7.207 0 0 0 0-10.192ZM12 7.999a3.002 3.002 0 1 1 0 6.004 3.002 3.002 0 0 1 0-6.003Zm0 1.5a1.501 1.501 0 1 0 0 3.004 1.501 1.501 0 0 0 0-3.003Z",
|
"location-outline": "M5.843 4.568a8.707 8.707 0 1 1 12.314 12.314l-1.187 1.174c-.875.858-2.01 1.962-3.406 3.312a2.25 2.25 0 0 1-3.128 0l-3.491-3.396c-.439-.431-.806-.794-1.102-1.09a8.707 8.707 0 0 1 0-12.314Zm11.253 1.06A7.207 7.207 0 1 0 6.904 15.822L8.39 17.29a753.98 753.98 0 0 0 3.088 3 .75.75 0 0 0 1.043 0l3.394-3.3c.47-.461.863-.85 1.18-1.168a7.207 7.207 0 0 0 0-10.192ZM12 7.999a3.002 3.002 0 1 1 0 6.004 3.002 3.002 0 0 1 0-6.003Zm0 1.5a1.501 1.501 0 1 0 0 3.004 1.501 1.501 0 0 0 0-3.003Z",
|
||||||
"lock-closed-outline": "M12 2a4 4 0 0 1 4 4v2h1.75A2.25 2.25 0 0 1 20 10.25v9.5A2.25 2.25 0 0 1 17.75 22H6.25A2.25 2.25 0 0 1 4 19.75v-9.5A2.25 2.25 0 0 1 6.25 8H8V6a4 4 0 0 1 4-4Zm5.75 7.5H6.25a.75.75 0 0 0-.75.75v9.5c0 .414.336.75.75.75h11.5a.75.75 0 0 0 .75-.75v-9.5a.75.75 0 0 0-.75-.75Zm-5.75 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm0-10A2.5 2.5 0 0 0 9.5 6v2h5V6A2.5 2.5 0 0 0 12 3.5Z",
|
"lock-closed-outline": "M12 2a4 4 0 0 1 4 4v2h1.75A2.25 2.25 0 0 1 20 10.25v9.5A2.25 2.25 0 0 1 17.75 22H6.25A2.25 2.25 0 0 1 4 19.75v-9.5A2.25 2.25 0 0 1 6.25 8H8V6a4 4 0 0 1 4-4Zm5.75 7.5H6.25a.75.75 0 0 0-.75.75v9.5c0 .414.336.75.75.75h11.5a.75.75 0 0 0 .75-.75v-9.5a.75.75 0 0 0-.75-.75Zm-5.75 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm0-10A2.5 2.5 0 0 0 9.5 6v2h5V6A2.5 2.5 0 0 0 12 3.5Z",
|
||||||
"mail-inbox-all-outline": "M6.25 3h11.5a3.25 3.25 0 0 1 3.245 3.066L21 6.25v11.5a3.25 3.25 0 0 1-3.066 3.245L17.75 21H6.25a3.25 3.25 0 0 1-3.245-3.066L3 17.75V6.25a3.25 3.25 0 0 1 3.066-3.245L6.25 3Zm2.075 11.5H4.5v3.25a1.75 1.75 0 0 0 1.606 1.744l.144.006h11.5a1.75 1.75 0 0 0 1.744-1.607l.006-.143V14.5h-3.825a3.752 3.752 0 0 1-3.475 2.995l-.2.005a3.752 3.752 0 0 1-3.632-2.812l-.043-.188Zm9.425-10H6.25a1.75 1.75 0 0 0-1.744 1.606L4.5 6.25V13H9a.75.75 0 0 1 .743.648l.007.102a2.25 2.25 0 0 0 4.495.154l.005-.154a.75.75 0 0 1 .648-.743L15 13h4.5V6.25a1.75 1.75 0 0 0-1.607-1.744L17.75 4.5Zm-11 5h10.5a.75.75 0 0 1 .102 1.493L17.25 11H6.75a.75.75 0 0 1-.102-1.493L6.75 9.5h10.5-10.5Zm0-3h10.5a.75.75 0 0 1 .102 1.493L17.25 8H6.75a.75.75 0 0 1-.102-1.493L6.75 6.5h10.5-10.5Z",
|
"mail-inbox-all-outline": "M6.25 3h11.5a3.25 3.25 0 0 1 3.245 3.066L21 6.25v11.5a3.25 3.25 0 0 1-3.066 3.245L17.75 21H6.25a3.25 3.25 0 0 1-3.245-3.066L3 17.75V6.25a3.25 3.25 0 0 1 3.066-3.245L6.25 3Zm2.075 11.5H4.5v3.25a1.75 1.75 0 0 0 1.606 1.744l.144.006h11.5a1.75 1.75 0 0 0 1.744-1.607l.006-.143V14.5h-3.825a3.752 3.752 0 0 1-3.475 2.995l-.2.005a3.752 3.752 0 0 1-3.632-2.812l-.043-.188Zm9.425-10H6.25a1.75 1.75 0 0 0-1.744 1.606L4.5 6.25V13H9a.75.75 0 0 1 .743.648l.007.102a2.25 2.25 0 0 0 4.495.154l.005-.154a.75.75 0 0 1 .648-.743L15 13h4.5V6.25a1.75 1.75 0 0 0-1.607-1.744L17.75 4.5Zm-11 5h10.5a.75.75 0 0 1 .102 1.493L17.25 11H6.75a.75.75 0 0 1-.102-1.493L6.75 9.5h10.5-10.5Zm0-3h10.5a.75.75 0 0 1 .102 1.493L17.25 8H6.75a.75.75 0 0 1-.102-1.493L6.75 6.5h10.5-10.5Z",
|
||||||
|
"mail-unread-outline": "M16 6.5H5.25a1.75 1.75 0 0 0-1.744 1.606l-.004.1L11 12.153l6.03-3.174a3.489 3.489 0 0 0 2.97.985v6.786a3.25 3.25 0 0 1-3.066 3.245L16.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75v-8.5a3.25 3.25 0 0 1 3.066-3.245L5.25 5h11.087A3.487 3.487 0 0 0 16 6.5Zm2.5 3.399-7.15 3.765a.75.75 0 0 1-.603.042l-.096-.042L3.5 9.9v6.85a1.75 1.75 0 0 0 1.606 1.744l.144.006h11.5a1.75 1.75 0 0 0 1.744-1.607l.006-.143V9.899ZM19.5 4a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z",
|
||||||
"mail-outline": "M5.25 4h13.5a3.25 3.25 0 0 1 3.245 3.066L22 7.25v9.5a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75v-9.5a3.25 3.25 0 0 1 3.066-3.245L5.25 4h13.5-13.5ZM20.5 9.373l-8.15 4.29a.75.75 0 0 1-.603.043l-.096-.042L3.5 9.374v7.376a1.75 1.75 0 0 0 1.606 1.744l.144.006h13.5a1.75 1.75 0 0 0 1.744-1.607l.006-.143V9.373ZM18.75 5.5H5.25a1.75 1.75 0 0 0-1.744 1.606L3.5 7.25v.429l8.5 4.473 8.5-4.474V7.25a1.75 1.75 0 0 0-1.607-1.744L18.75 5.5Z",
|
"mail-outline": "M5.25 4h13.5a3.25 3.25 0 0 1 3.245 3.066L22 7.25v9.5a3.25 3.25 0 0 1-3.066 3.245L18.75 20H5.25a3.25 3.25 0 0 1-3.245-3.066L2 16.75v-9.5a3.25 3.25 0 0 1 3.066-3.245L5.25 4h13.5-13.5ZM20.5 9.373l-8.15 4.29a.75.75 0 0 1-.603.043l-.096-.042L3.5 9.374v7.376a1.75 1.75 0 0 0 1.606 1.744l.144.006h13.5a1.75 1.75 0 0 0 1.744-1.607l.006-.143V9.373ZM18.75 5.5H5.25a1.75 1.75 0 0 0-1.744 1.606L3.5 7.25v.429l8.5 4.473 8.5-4.474V7.25a1.75 1.75 0 0 0-1.607-1.744L18.75 5.5Z",
|
||||||
"map-outline": "m9.203 4 .047-.002.046.001a.73.73 0 0 1 .067.007l.016.004c.086.014.17.044.252.092l.051.034 5.07 3.565L19.82 4.14a.75.75 0 0 1 1.174.51l.007.104v10.632a.75.75 0 0 1-.238.548l-.08.066-5.5 3.866a.744.744 0 0 1-.828.023L9.25 16.297l-5.07 3.565a.75.75 0 0 1-1.174-.51l-.007-.104V8.616a.75.75 0 0 1 .238-.548l.08-.066 5.5-3.866a.762.762 0 0 1 .2-.101l.122-.028.064-.008Zm10.298 2.197-4 2.812v8.799l4-2.812v-8.8ZM8.5 6.193l-4 2.812v8.8l4-2.813V6.193Zm1.502 0v8.8l4 2.811V9.005l-4-2.812Z",
|
"map-outline": "m9.203 4 .047-.002.046.001a.73.73 0 0 1 .067.007l.016.004c.086.014.17.044.252.092l.051.034 5.07 3.565L19.82 4.14a.75.75 0 0 1 1.174.51l.007.104v10.632a.75.75 0 0 1-.238.548l-.08.066-5.5 3.866a.744.744 0 0 1-.828.023L9.25 16.297l-5.07 3.565a.75.75 0 0 1-1.174-.51l-.007-.104V8.616a.75.75 0 0 1 .238-.548l.08-.066 5.5-3.866a.762.762 0 0 1 .2-.101l.122-.028.064-.008Zm10.298 2.197-4 2.812v8.799l4-2.812v-8.8ZM8.5 6.193l-4 2.812v8.8l4-2.813V6.193Zm1.502 0v8.8l4 2.811V9.005l-4-2.812Z",
|
||||||
"megaphone-outline": "M21.907 5.622c.062.208.093.424.093.641V17.74a2.25 2.25 0 0 1-2.891 2.156l-5.514-1.64a4.002 4.002 0 0 1-7.59-1.556L6 16.5l-.001-.5-2.39-.711A2.25 2.25 0 0 1 2 13.131V10.87a2.25 2.25 0 0 1 1.61-2.156l15.5-4.606a2.25 2.25 0 0 1 2.797 1.515ZM7.499 16.445l.001.054a2.5 2.5 0 0 0 4.624 1.321l-4.625-1.375Zm12.037-10.9-15.5 4.605a.75.75 0 0 0-.536.72v2.261a.75.75 0 0 0 .536.72l15.5 4.607a.75.75 0 0 0 .964-.72V6.264a.75.75 0 0 0-.964-.719Z",
|
"megaphone-outline": "M21.907 5.622c.062.208.093.424.093.641V17.74a2.25 2.25 0 0 1-2.891 2.156l-5.514-1.64a4.002 4.002 0 0 1-7.59-1.556L6 16.5l-.001-.5-2.39-.711A2.25 2.25 0 0 1 2 13.131V10.87a2.25 2.25 0 0 1 1.61-2.156l15.5-4.606a2.25 2.25 0 0 1 2.797 1.515ZM7.499 16.445l.001.054a2.5 2.5 0 0 0 4.624 1.321l-4.625-1.375Zm12.037-10.9-15.5 4.605a.75.75 0 0 0-.536.72v2.261a.75.75 0 0 0 .536.72l15.5 4.607a.75.75 0 0 0 .964-.72V6.264a.75.75 0 0 0-.964-.719Z",
|
||||||
|
|
|
@ -36,9 +36,9 @@ class ReportingEventListener < BaseListener
|
||||||
event_start_time: conversation.created_at,
|
event_start_time: conversation.created_at,
|
||||||
event_end_time: message.created_at
|
event_end_time: message.created_at
|
||||||
)
|
)
|
||||||
# rubocop:disable Rails/SkipsModelValidations
|
|
||||||
conversation.update_columns(first_reply_created_at: message.created_at)
|
conversation.update(first_reply_created_at: message.created_at)
|
||||||
# rubocop:enable Rails/SkipsModelValidations
|
|
||||||
reporting_event.save!
|
reporting_event.save!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -220,7 +220,7 @@ class Conversation < ApplicationRecord
|
||||||
|
|
||||||
def notify_conversation_updation
|
def notify_conversation_updation
|
||||||
return unless previous_changes.keys.present? && (previous_changes.keys & %w[team_id assignee_id status snoozed_until
|
return unless previous_changes.keys.present? && (previous_changes.keys & %w[team_id assignee_id status snoozed_until
|
||||||
custom_attributes label_list]).present?
|
custom_attributes label_list first_reply_created_at]).present?
|
||||||
|
|
||||||
dispatcher_dispatch(CONVERSATION_UPDATED, previous_changes)
|
dispatcher_dispatch(CONVERSATION_UPDATED, previous_changes)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,7 @@ class Conversations::EventDataPresenter < SimpleDelegator
|
||||||
custom_attributes: custom_attributes,
|
custom_attributes: custom_attributes,
|
||||||
snoozed_until: snoozed_until,
|
snoozed_until: snoozed_until,
|
||||||
unread_count: unread_incoming_messages.count,
|
unread_count: unread_incoming_messages.count,
|
||||||
|
first_reply_created_at: first_reply_created_at,
|
||||||
**push_timestamps
|
**push_timestamps
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,12 +3,7 @@ require 'json'
|
||||||
class FilterService
|
class FilterService
|
||||||
ATTRIBUTE_MODEL = 'conversation_attribute'.freeze
|
ATTRIBUTE_MODEL = 'conversation_attribute'.freeze
|
||||||
ATTRIBUTE_TYPES = {
|
ATTRIBUTE_TYPES = {
|
||||||
date: 'date',
|
date: 'date', text: 'text', number: 'numeric', link: 'text', list: 'text', checkbox: 'boolean'
|
||||||
text: 'text',
|
|
||||||
number: 'numeric',
|
|
||||||
link: 'text',
|
|
||||||
list: 'text',
|
|
||||||
checkbox: 'boolean'
|
|
||||||
}.with_indifferent_access
|
}.with_indifferent_access
|
||||||
|
|
||||||
def initialize(params, user)
|
def initialize(params, user)
|
||||||
|
@ -60,7 +55,7 @@ class FilterService
|
||||||
end
|
end
|
||||||
|
|
||||||
def case_insensitive_values(query_hash)
|
def case_insensitive_values(query_hash)
|
||||||
if query_hash['custom_attribute_type'].present? && query_hash['values'][0].is_a?(String)
|
if @custom_attribute_type.present? && query_hash['values'][0].is_a?(String)
|
||||||
string_filter_values(query_hash)
|
string_filter_values(query_hash)
|
||||||
else
|
else
|
||||||
query_hash['values']
|
query_hash['values']
|
||||||
|
@ -125,11 +120,13 @@ class FilterService
|
||||||
query_operator = query_hash[:query_operator]
|
query_operator = query_hash[:query_operator]
|
||||||
table_name = attribute_model == 'conversation_attribute' ? 'conversations' : 'contacts'
|
table_name = attribute_model == 'conversation_attribute' ? 'conversations' : 'contacts'
|
||||||
|
|
||||||
if attribute_data_type == 'text'
|
query = if attribute_data_type == 'text'
|
||||||
" LOWER(#{table_name}.custom_attributes ->> '#{@attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
|
" LOWER(#{table_name}.custom_attributes ->> '#{@attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
|
||||||
else
|
else
|
||||||
" (#{table_name}.custom_attributes ->> '#{@attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
|
" (#{table_name}.custom_attributes ->> '#{@attribute_key}')::#{attribute_data_type} #{filter_operator_value} #{query_operator} "
|
||||||
end
|
end
|
||||||
|
|
||||||
|
query + not_in_custom_attr_query(table_name, query_hash, attribute_data_type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_attribute(attribute_key, account, custom_attribute_type)
|
def custom_attribute(attribute_key, account, custom_attribute_type)
|
||||||
|
@ -140,6 +137,12 @@ class FilterService
|
||||||
).find_by(attribute_key: attribute_key)
|
).find_by(attribute_key: attribute_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def not_in_custom_attr_query(table_name, query_hash, attribute_data_type)
|
||||||
|
return '' unless query_hash[:filter_operator] == 'not_equal_to'
|
||||||
|
|
||||||
|
" OR (#{table_name}.custom_attributes ->> '#{@attribute_key}')::#{attribute_data_type} IS NULL "
|
||||||
|
end
|
||||||
|
|
||||||
def equals_to_filter_string(filter_operator, current_index)
|
def equals_to_filter_string(filter_operator, current_index)
|
||||||
return "IN (:value_#{current_index})" if filter_operator == 'equal_to'
|
return "IN (:value_#{current_index})" if filter_operator == 'equal_to'
|
||||||
|
|
||||||
|
|
|
@ -38,5 +38,6 @@ json.muted conversation.muted?
|
||||||
json.snoozed_until conversation.snoozed_until
|
json.snoozed_until conversation.snoozed_until
|
||||||
json.status conversation.status
|
json.status conversation.status
|
||||||
json.timestamp conversation.last_activity_at.to_i
|
json.timestamp conversation.last_activity_at.to_i
|
||||||
|
json.first_reply_created_at conversation.first_reply_created_at.to_i
|
||||||
json.unread_count conversation.unread_incoming_messages.count
|
json.unread_count conversation.unread_incoming_messages.count
|
||||||
json.last_non_activity_message conversation.messages.non_activity_messages.first.try(:push_event_data)
|
json.last_non_activity_message conversation.messages.non_activity_messages.first.try(:push_event_data)
|
||||||
|
|
|
@ -33,7 +33,7 @@ json.website_token resource.channel.try(:website_token)
|
||||||
json.selected_feature_flags resource.channel.try(:selected_feature_flags)
|
json.selected_feature_flags resource.channel.try(:selected_feature_flags)
|
||||||
json.reply_time resource.channel.try(:reply_time)
|
json.reply_time resource.channel.try(:reply_time)
|
||||||
if resource.web_widget?
|
if resource.web_widget?
|
||||||
json.hmac_token resource.channel.try(:hmac_token)
|
json.hmac_token resource.channel.try(:hmac_token) if Current.account_user&.administrator?
|
||||||
json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled)
|
json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled)
|
||||||
json.pre_chat_form_options resource.channel.try(:pre_chat_form_options)
|
json.pre_chat_form_options resource.channel.try(:pre_chat_form_options)
|
||||||
json.continuity_via_email resource.channel.try(:continuity_via_email)
|
json.continuity_via_email resource.channel.try(:continuity_via_email)
|
||||||
|
@ -56,14 +56,17 @@ if resource.email?
|
||||||
json.email resource.channel.try(:email)
|
json.email resource.channel.try(:email)
|
||||||
|
|
||||||
## IMAP
|
## IMAP
|
||||||
|
if Current.account_user&.administrator?
|
||||||
json.imap_login resource.channel.try(:imap_login)
|
json.imap_login resource.channel.try(:imap_login)
|
||||||
json.imap_password resource.channel.try(:imap_password)
|
json.imap_password resource.channel.try(:imap_password)
|
||||||
json.imap_address resource.channel.try(:imap_address)
|
json.imap_address resource.channel.try(:imap_address)
|
||||||
json.imap_port resource.channel.try(:imap_port)
|
json.imap_port resource.channel.try(:imap_port)
|
||||||
json.imap_enabled resource.channel.try(:imap_enabled)
|
json.imap_enabled resource.channel.try(:imap_enabled)
|
||||||
json.imap_enable_ssl resource.channel.try(:imap_enable_ssl)
|
json.imap_enable_ssl resource.channel.try(:imap_enable_ssl)
|
||||||
|
end
|
||||||
|
|
||||||
## SMTP
|
## SMTP
|
||||||
|
if Current.account_user&.administrator?
|
||||||
json.smtp_login resource.channel.try(:smtp_login)
|
json.smtp_login resource.channel.try(:smtp_login)
|
||||||
json.smtp_password resource.channel.try(:smtp_password)
|
json.smtp_password resource.channel.try(:smtp_password)
|
||||||
json.smtp_address resource.channel.try(:smtp_address)
|
json.smtp_address resource.channel.try(:smtp_address)
|
||||||
|
@ -74,11 +77,12 @@ if resource.email?
|
||||||
json.smtp_enable_starttls_auto resource.channel.try(:smtp_enable_starttls_auto)
|
json.smtp_enable_starttls_auto resource.channel.try(:smtp_enable_starttls_auto)
|
||||||
json.smtp_openssl_verify_mode resource.channel.try(:smtp_openssl_verify_mode)
|
json.smtp_openssl_verify_mode resource.channel.try(:smtp_openssl_verify_mode)
|
||||||
json.smtp_authentication resource.channel.try(:smtp_authentication)
|
json.smtp_authentication resource.channel.try(:smtp_authentication)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
## API Channel Attributes
|
## API Channel Attributes
|
||||||
if resource.api?
|
if resource.api?
|
||||||
json.hmac_token resource.channel.try(:hmac_token)
|
json.hmac_token resource.channel.try(:hmac_token) if Current.account_user&.administrator?
|
||||||
json.webhook_url resource.channel.try(:webhook_url)
|
json.webhook_url resource.channel.try(:webhook_url)
|
||||||
json.inbox_identifier resource.channel.try(:identifier)
|
json.inbox_identifier resource.channel.try(:identifier)
|
||||||
json.additional_attributes resource.channel.try(:additional_attributes)
|
json.additional_attributes resource.channel.try(:additional_attributes)
|
||||||
|
|
|
@ -53,7 +53,7 @@ RSpec.describe 'Conversations API', type: :request do
|
||||||
|
|
||||||
get "/api/v1/accounts/#{account.id}/conversations",
|
get "/api/v1/accounts/#{account.id}/conversations",
|
||||||
headers: agent_1.create_new_auth_token,
|
headers: agent_1.create_new_auth_token,
|
||||||
params: { reply_status: 'unattended' },
|
params: { conversation_type: 'unattended' },
|
||||||
as: :json
|
as: :json
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
|
|
|
@ -103,7 +103,62 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||||
as: :json
|
as: :json
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(JSON.parse(response.body, symbolize_names: true)[:id]).to eq(inbox.id)
|
data = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
expect(data[:id]).to eq(inbox.id)
|
||||||
|
expect(data[:hmac_token]).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns empty imap details in inbox when agent' do
|
||||||
|
email_channel = create(:channel_email, account: account, imap_enabled: true, imap_login: 'test@test.com')
|
||||||
|
email_inbox = create(:inbox, channel: email_channel, account: account)
|
||||||
|
create(:inbox_member, user: agent, inbox: email_inbox)
|
||||||
|
|
||||||
|
imap_connection = double
|
||||||
|
allow(Mail).to receive(:connection).and_return(imap_connection)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
data = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
|
||||||
|
expect(data[:imap_enabled]).to be_nil
|
||||||
|
expect(data[:imap_login]).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns imap details in inbox when admin' do
|
||||||
|
email_channel = create(:channel_email, account: account, imap_enabled: true, imap_login: 'test@test.com')
|
||||||
|
email_inbox = create(:inbox, channel: email_channel, account: account)
|
||||||
|
|
||||||
|
imap_connection = double
|
||||||
|
allow(Mail).to receive(:connection).and_return(imap_connection)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/inboxes/#{email_inbox.id}",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
data = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
|
||||||
|
expect(data[:imap_enabled]).to be_truthy
|
||||||
|
expect(data[:imap_login]).to eq('test@test.com')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fetch API inbox without hmac token when agent' do
|
||||||
|
api_channel = create(:channel_api, account: account)
|
||||||
|
api_inbox = create(:inbox, channel: api_channel, account: account)
|
||||||
|
create(:inbox_member, user: agent, inbox: api_inbox)
|
||||||
|
|
||||||
|
get "/api/v1/accounts/#{account.id}/inboxes/#{api_inbox.id}",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
|
||||||
|
data = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
|
||||||
|
expect(data[:hmac_token]).to be_nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -136,5 +136,15 @@ describe ::ConversationFinder do
|
||||||
expect(result[:conversations].length).to be 25
|
expect(result[:conversations].length).to be 25
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with unattended' do
|
||||||
|
let(:params) { { status: 'open', assignee_type: 'me', conversation_type: 'unattended' } }
|
||||||
|
|
||||||
|
it 'returns unattended conversations' do
|
||||||
|
create_list(:conversation, 25, account: account, inbox: inbox, assignee: user_1)
|
||||||
|
result = conversation_finder.perform
|
||||||
|
expect(result[:conversations].length).to be 25
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -455,6 +455,7 @@ RSpec.describe Conversation, type: :model do
|
||||||
channel: 'Channel::WebWidget',
|
channel: 'Channel::WebWidget',
|
||||||
snoozed_until: conversation.snoozed_until,
|
snoozed_until: conversation.snoozed_until,
|
||||||
custom_attributes: conversation.custom_attributes,
|
custom_attributes: conversation.custom_attributes,
|
||||||
|
first_reply_created_at: nil,
|
||||||
contact_last_seen_at: conversation.contact_last_seen_at.to_i,
|
contact_last_seen_at: conversation.contact_last_seen_at.to_i,
|
||||||
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
|
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
|
||||||
unread_count: 0
|
unread_count: 0
|
||||||
|
|
|
@ -27,6 +27,7 @@ RSpec.describe Conversations::EventDataPresenter do
|
||||||
timestamp: conversation.last_activity_at.to_i,
|
timestamp: conversation.last_activity_at.to_i,
|
||||||
snoozed_until: conversation.snoozed_until,
|
snoozed_until: conversation.snoozed_until,
|
||||||
custom_attributes: conversation.custom_attributes,
|
custom_attributes: conversation.custom_attributes,
|
||||||
|
first_reply_created_at: nil,
|
||||||
contact_last_seen_at: conversation.contact_last_seen_at.to_i,
|
contact_last_seen_at: conversation.contact_last_seen_at.to_i,
|
||||||
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
|
agent_last_seen_at: conversation.agent_last_seen_at.to_i,
|
||||||
unread_count: 0
|
unread_count: 0
|
||||||
|
|
|
@ -86,6 +86,17 @@ describe ::Conversations::FilterService do
|
||||||
expect(result.length).to be conversations.count
|
expect(result.length).to be conversations.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'filter conversations by additional_attributes with NOT_IN filter' do
|
||||||
|
payload = [{ attribute_key: 'conversation_type', filter_operator: 'not_equal_to', values: 'platinum', query_operator: nil,
|
||||||
|
custom_attribute_type: 'conversation_attribute' }.with_indifferent_access]
|
||||||
|
params[:payload] = payload
|
||||||
|
result = filter_service.new(params, user_1).perform
|
||||||
|
conversations = Conversation.where(
|
||||||
|
"custom_attributes ->> 'conversation_type' NOT IN (?) OR custom_attributes ->> 'conversation_type' IS NULL", ['platinum']
|
||||||
|
)
|
||||||
|
expect(result[:count][:all_count]).to be conversations.count
|
||||||
|
end
|
||||||
|
|
||||||
it 'filter conversations by tags' do
|
it 'filter conversations by tags' do
|
||||||
unassigned_conversation.update_labels('support')
|
unassigned_conversation.update_labels('support')
|
||||||
params[:payload] = [
|
params[:payload] = [
|
||||||
|
|
Loading…
Reference in a new issue