From 7a7aa0ef4dfae0d06a46435cef3f10ae91356f6f Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 12 Oct 2023 09:53:24 -0600 Subject: [PATCH 01/22] users/agent endpoint now public for forwarding --- apps/meteor/app/livechat/imports/server/rest/users.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/meteor/app/livechat/imports/server/rest/users.ts b/apps/meteor/app/livechat/imports/server/rest/users.ts index 680bdc7e80d2..a7fe45f3ec86 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.ts +++ b/apps/meteor/app/livechat/imports/server/rest/users.ts @@ -14,10 +14,6 @@ API.v1.addRoute( { authRequired: true, permissionsRequired: { - GET: { - permissions: ['manage-livechat-agents'], - operation: 'hasAll', - }, POST: { permissions: ['view-livechat-manager'], operation: 'hasAll' }, }, validateParams: { From 782180c1ee883ab95d093d584636707d0444db25 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 12 Oct 2023 09:56:43 -0600 Subject: [PATCH 02/22] Create rotten-dryers-allow.md --- .changeset/rotten-dryers-allow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rotten-dryers-allow.md diff --git a/.changeset/rotten-dryers-allow.md b/.changeset/rotten-dryers-allow.md new file mode 100644 index 000000000000..5bb7d69d3408 --- /dev/null +++ b/.changeset/rotten-dryers-allow.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +chore: Use `users/agent` endpoint in forwarding modals From 589b3dfb758bd14c89bb9f0ead3f639c385f044e Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 12 Oct 2023 10:55:55 -0600 Subject: [PATCH 03/22] onlyavailable --- apps/meteor/app/livechat/imports/server/rest/users.ts | 2 ++ apps/meteor/app/livechat/server/api/lib/users.ts | 11 +++++++++++ packages/rest-typings/src/v1/omnichannel.ts | 8 ++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/apps/meteor/app/livechat/imports/server/rest/users.ts b/apps/meteor/app/livechat/imports/server/rest/users.ts index a7fe45f3ec86..99a7bdf079f3 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.ts +++ b/apps/meteor/app/livechat/imports/server/rest/users.ts @@ -35,9 +35,11 @@ API.v1.addRoute( return API.v1.unauthorized(); } + const { onlyAvailable } = this.queryParams; return API.v1.success( await findAgents({ text, + onlyAvailable, pagination: { offset, count, diff --git a/apps/meteor/app/livechat/server/api/lib/users.ts b/apps/meteor/app/livechat/server/api/lib/users.ts index 948cde0f8bfd..a697962ca841 100644 --- a/apps/meteor/app/livechat/server/api/lib/users.ts +++ b/apps/meteor/app/livechat/server/api/lib/users.ts @@ -10,10 +10,12 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; async function findUsers({ role, text, + onlyAvailable = false, pagination: { offset, count, sort }, }: { role: IRole['_id']; text?: string; + onlyAvailable?: boolean; pagination: { offset: number; count: number; sort: any }; }): Promise<{ users: ILivechatAgent[]; count: number; offset: number; total: number }> { const query = {}; @@ -24,6 +26,12 @@ async function findUsers({ }); } + if (onlyAvailable) { + Object.assign(query, { + statusLivechat: 'available', + }); + } + const [ { sortedResults, @@ -52,14 +60,17 @@ async function findUsers({ } export async function findAgents({ text, + onlyAvailable = false, pagination: { offset, count, sort }, }: { text?: string; + onlyAvailable: boolean; pagination: { offset: number; count: number; sort: any }; }): Promise> { return findUsers({ role: 'livechat-agent', text, + onlyAvailable, pagination: { offset, count, diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index bebea2856861..71fdfbcab559 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -749,7 +749,7 @@ const LivechatDepartmentsByUnitIdSchema = { export const isLivechatDepartmentsByUnitIdProps = ajv.compile(LivechatDepartmentsByUnitIdSchema); -type LivechatUsersManagerGETProps = PaginatedRequest<{ text?: string; fields?: string }>; +type LivechatUsersManagerGETProps = PaginatedRequest<{ text?: string; fields?: string; onlyAvailable?: boolean }>; const LivechatUsersManagerGETSchema = { type: 'object', @@ -758,6 +758,10 @@ const LivechatUsersManagerGETSchema = { type: 'string', nullable: true, }, + onlyAvailable: { + type: 'string', + nullable: true, + }, count: { type: 'number', nullable: true, @@ -3386,7 +3390,7 @@ export type OmnichannelEndpoints = { }; '/v1/livechat/users/agent': { - GET: (params: PaginatedRequest<{ text?: string }>) => PaginatedResult<{ + GET: (params: PaginatedRequest<{ text?: string; onlyAvailable?: boolean }>) => PaginatedResult<{ users: (ILivechatAgent & { departments: string[] })[]; }>; POST: (params: LivechatUsersManagerPOSTProps) => { success: boolean }; From aae452c2fa9ea95e997887654a2cff72b972b373 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 12 Oct 2023 11:00:05 -0600 Subject: [PATCH 04/22] test --- .../end-to-end/api/livechat/01-agents.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts b/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts index 83efe2c96aa2..6f9120ea9094 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/01-agents.ts @@ -94,6 +94,26 @@ describe('LIVECHAT - Agents', function () { expect(agentRecentlyCreated?._id).to.be.equal(agent._id); }); }); + it('should return an array of available agents', async () => { + await updatePermission('edit-omnichannel-contact', ['admin']); + await updatePermission('transfer-livechat-guest', ['admin']); + await updatePermission('manage-livechat-agents', ['admin']); + + await request + .get(api('livechat/users/agent')) + .set(credentials) + .expect('Content-Type', 'application/json') + .query({ onlyAvailable: true }) + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.property('success', true); + expect(res.body.users).to.be.an('array'); + expect(res.body).to.have.property('offset'); + expect(res.body).to.have.property('total'); + expect(res.body).to.have.property('count'); + expect(res.body.users.every((u: { statusLivechat: string }) => u.statusLivechat === 'available')).to.be.true; + }); + }); it('should return an array of managers', async () => { await updatePermission('view-livechat-manager', ['admin']); await updatePermission('manage-livechat-agents', ['admin']); From 34d83879f66e161c490dc6880da6553c53dce6c0 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Thu, 12 Oct 2023 12:10:59 -0600 Subject: [PATCH 05/22] good permission --- apps/meteor/app/livechat/imports/server/rest/users.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/meteor/app/livechat/imports/server/rest/users.ts b/apps/meteor/app/livechat/imports/server/rest/users.ts index 99a7bdf079f3..ebd368613ef5 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.ts +++ b/apps/meteor/app/livechat/imports/server/rest/users.ts @@ -9,12 +9,15 @@ import { hasAtLeastOnePermissionAsync } from '../../../../authorization/server/f import { findAgents, findManagers } from '../../../server/api/lib/users'; import { Livechat } from '../../../server/lib/Livechat'; +const emptyStringArray: string[] = []; + API.v1.addRoute( 'livechat/users/:type', { authRequired: true, permissionsRequired: { - POST: { permissions: ['view-livechat-manager'], operation: 'hasAll' }, + 'POST': ['view-livechat-manager'], + '*': emptyStringArray, }, validateParams: { GET: isLivechatUsersManagerGETProps, From 4ccfbe4c8bc2c2af01deeff14f7a8491314679d5 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 13 Oct 2023 10:10:14 -0600 Subject: [PATCH 06/22] new filters --- .../app/livechat/imports/server/rest/users.ts | 4 +- .../app/livechat/server/api/lib/users.ts | 41 ++++++++++++++++++- packages/rest-typings/src/v1/omnichannel.ts | 16 +++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/apps/meteor/app/livechat/imports/server/rest/users.ts b/apps/meteor/app/livechat/imports/server/rest/users.ts index ebd368613ef5..196970583249 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.ts +++ b/apps/meteor/app/livechat/imports/server/rest/users.ts @@ -38,11 +38,13 @@ API.v1.addRoute( return API.v1.unauthorized(); } - const { onlyAvailable } = this.queryParams; + const { onlyAvailable, excludeId, showIdleAgents } = this.queryParams; return API.v1.success( await findAgents({ text, onlyAvailable, + excludeId, + showIdleAgents, pagination: { offset, count, diff --git a/apps/meteor/app/livechat/server/api/lib/users.ts b/apps/meteor/app/livechat/server/api/lib/users.ts index a697962ca841..2fc947f9ca45 100644 --- a/apps/meteor/app/livechat/server/api/lib/users.ts +++ b/apps/meteor/app/livechat/server/api/lib/users.ts @@ -1,6 +1,7 @@ import type { ILivechatAgent, IRole } from '@rocket.chat/core-typings'; import { Users } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; +import type { FilterOperators } from 'mongodb'; /** * @param {IRole['_id']} role the role id @@ -11,14 +12,18 @@ async function findUsers({ role, text, onlyAvailable = false, + excludeId, + showIdleAgents = true, pagination: { offset, count, sort }, }: { role: IRole['_id']; text?: string; onlyAvailable?: boolean; + excludeId?: string; + showIdleAgents?: boolean; pagination: { offset: number; count: number; sort: any }; }): Promise<{ users: ILivechatAgent[]; count: number; offset: number; total: number }> { - const query = {}; + const query: FilterOperators = {}; if (text) { const filterReg = new RegExp(escapeRegExp(text), 'i'); Object.assign(query, { @@ -32,6 +37,34 @@ async function findUsers({ }); } + if (excludeId) { + Object.assign(query, { + _id: { $ne: excludeId }, + }); + } + + if (!showIdleAgents) { + const oldOr = query.$or; + + if (oldOr) { + delete query.$or; + Object.assign(query, { + $and: [ + { + $or: oldOr, + }, + { + $or: [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }], + }, + ], + }); + } else { + Object.assign(query, { + $or: [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }], + }); + } + } + const [ { sortedResults, @@ -61,16 +94,22 @@ async function findUsers({ export async function findAgents({ text, onlyAvailable = false, + excludeId, + showIdleAgents = true, pagination: { offset, count, sort }, }: { text?: string; onlyAvailable: boolean; + excludeId?: string; + showIdleAgents?: boolean; pagination: { offset: number; count: number; sort: any }; }): Promise> { return findUsers({ role: 'livechat-agent', text, onlyAvailable, + excludeId, + showIdleAgents, pagination: { offset, count, diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index 71fdfbcab559..3adf8b1d6f37 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -749,7 +749,13 @@ const LivechatDepartmentsByUnitIdSchema = { export const isLivechatDepartmentsByUnitIdProps = ajv.compile(LivechatDepartmentsByUnitIdSchema); -type LivechatUsersManagerGETProps = PaginatedRequest<{ text?: string; fields?: string; onlyAvailable?: boolean }>; +type LivechatUsersManagerGETProps = PaginatedRequest<{ + text?: string; + fields?: string; + onlyAvailable?: boolean; + excludeId?: string; + showIdleAgents?: boolean; +}>; const LivechatUsersManagerGETSchema = { type: 'object', @@ -762,6 +768,14 @@ const LivechatUsersManagerGETSchema = { type: 'string', nullable: true, }, + excludeId: { + type: 'string', + nullable: true, + }, + showIdleAgents: { + type: 'boolean', + nullable: true, + }, count: { type: 'number', nullable: true, From 4b27bfc7e98f3ce2cd76eb920babb45cacb50269 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 13 Oct 2023 15:31:07 -0300 Subject: [PATCH 07/22] feat: added new props to AutoCompleteAgent --- .../client/components/AutoCompleteAgent.tsx | 27 ++++++++----------- .../Omnichannel/hooks/useAgentsList.ts | 15 ++++++++--- packages/rest-typings/src/v1/omnichannel.ts | 4 ++- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/apps/meteor/client/components/AutoCompleteAgent.tsx b/apps/meteor/client/components/AutoCompleteAgent.tsx index b4e287bcc4ae..860ee3024ed8 100644 --- a/apps/meteor/client/components/AutoCompleteAgent.tsx +++ b/apps/meteor/client/components/AutoCompleteAgent.tsx @@ -11,18 +11,24 @@ type AutoCompleteAgentProps = { value: string; error?: string; placeholder?: string; - onChange: (value: string) => void; haveAll?: boolean; haveNoAgentsSelectedOption?: boolean; + excludeId?: string; + showIdleAgents?: boolean; + onlyAvailable?: boolean; + onChange: (value: string) => void; }; const AutoCompleteAgent = ({ value, error, placeholder, - onChange, haveAll = false, haveNoAgentsSelectedOption = false, + excludeId, + showIdleAgents = false, + onlyAvailable = false, + onChange, }: AutoCompleteAgentProps): ReactElement => { const [agentsFilter, setAgentsFilter] = useState(''); @@ -30,24 +36,13 @@ const AutoCompleteAgent = ({ const { itemsList: AgentsList, loadMoreItems: loadMoreAgents } = useAgentsList( useMemo( - () => ({ text: debouncedAgentsFilter, haveAll, haveNoAgentsSelectedOption }), - [debouncedAgentsFilter, haveAll, haveNoAgentsSelectedOption], + () => ({ text: debouncedAgentsFilter, onlyAvailable, haveAll, haveNoAgentsSelectedOption, excludeId, showIdleAgents }), + [debouncedAgentsFilter, excludeId, haveAll, haveNoAgentsSelectedOption, onlyAvailable, showIdleAgents], ), ); const { phase: agentsPhase, itemCount: agentsTotal, items: agentsItems } = useRecordList(AgentsList); - const sortedByName = agentsItems.sort((a, b) => { - if (a.label > b.label) { - return 1; - } - if (a.label < b.label) { - return -1; - } - - return 0; - }); - return ( void} - options={sortedByName} + options={agentsItems} data-qa='autocomplete-agent' endReached={ agentsPhase === AsyncStatePhase.LOADING ? (): void => undefined : (start): void => loadMoreAgents(start, Math.min(50, agentsTotal)) diff --git a/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts b/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts index b854866184f7..2f0949c5c223 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts @@ -9,6 +9,9 @@ type AgentsListOptions = { text: string; haveAll: boolean; haveNoAgentsSelectedOption: boolean; + excludeId?: string; + showIdleAgents?: boolean; + onlyAvailable?: boolean; }; type AgentOption = { value: string; label: string; _updatedAt: Date; _id: string }; @@ -26,6 +29,7 @@ export const useAgentsList = ( const reload = useCallback(() => setItemsList(new RecordList()), []); const getAgents = useEndpoint('GET', '/v1/livechat/users/agent'); + const { text, onlyAvailable = false, showIdleAgents = false, excludeId, haveAll, haveNoAgentsSelectedOption } = options; useComponentDidUpdate(() => { options && reload(); @@ -34,7 +38,10 @@ export const useAgentsList = ( const fetchData = useCallback( async (start, end) => { const { users: agents, total } = await getAgents({ - ...(options.text && { text: options.text }), + ...(text && { text }), + ...(excludeId && { excludeId }), + showIdleAgents, + onlyAvailable, offset: start, count: end + start, sort: `{ "name": 1 }`, @@ -50,7 +57,7 @@ export const useAgentsList = ( return agentOption; }); - options.haveAll && + haveAll && items.unshift({ label: t('All'), value: 'all', @@ -58,7 +65,7 @@ export const useAgentsList = ( _id: 'all', }); - options.haveNoAgentsSelectedOption && + haveNoAgentsSelectedOption && items.unshift({ label: t('Empty_no_agent_selected'), value: 'no-agent-selected', @@ -71,7 +78,7 @@ export const useAgentsList = ( itemCount: total + 1, }; }, - [getAgents, options.haveAll, options.haveNoAgentsSelectedOption, options.text, t], + [excludeId, getAgents, haveAll, haveNoAgentsSelectedOption, onlyAvailable, showIdleAgents, t, text], ); const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25); diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index 3adf8b1d6f37..3baeae111202 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -3404,7 +3404,9 @@ export type OmnichannelEndpoints = { }; '/v1/livechat/users/agent': { - GET: (params: PaginatedRequest<{ text?: string; onlyAvailable?: boolean }>) => PaginatedResult<{ + GET: ( + params: PaginatedRequest<{ text?: string; onlyAvailable?: boolean; excludeId?: string; showIdleAgents?: boolean }>, + ) => PaginatedResult<{ users: (ILivechatAgent & { departments: string[] })[]; }>; POST: (params: LivechatUsersManagerPOSTProps) => { success: boolean }; From 75e52fd0e311d4f934c3b0d97b25dd894487d2af Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 13 Oct 2023 15:32:06 -0300 Subject: [PATCH 08/22] fix: changed UserAutoComplete to AutoCompleteAgent in ForwardChatModal --- .../Omnichannel/modals/ForwardChatModal.tsx | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index bdbde6b05acd..0784747d2f03 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -19,7 +19,7 @@ import { useForm } from 'react-hook-form'; import { useRecordList } from '../../../hooks/lists/useRecordList'; import { AsyncStatePhase } from '../../../hooks/useAsyncState'; -import UserAutoComplete from '../../UserAutoComplete'; +import AutoCompleteAgent from '../../AutoCompleteAgent'; import { useDepartmentsList } from '../hooks/useDepartmentsList'; const ForwardChatModal = ({ @@ -53,28 +53,6 @@ const ForwardChatModal = ({ ); const { phase: departmentsPhase, items: departments, itemCount: departmentsTotal } = useRecordList(departmentsList); - const _id = { $ne: room.servedBy?._id }; - const conditions = { - _id, - ...(!idleAgentsAllowedForForwarding && { - $or: [ - { - status: { - $exists: true, - $ne: 'offline', - }, - roles: { - $ne: 'bot', - }, - }, - { - roles: 'bot', - }, - ], - }), - statusLivechat: 'available', - }; - const endReached = useCallback( (start) => { if (departmentsPhase !== AsyncStatePhase.LOADING) { @@ -134,8 +112,10 @@ const ForwardChatModal = ({ {t('Forward_to_user')} - { setValue('username', value); From 2e26338c39574d2fe62a6614cbb702e286b13a81 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 13 Oct 2023 15:58:19 -0300 Subject: [PATCH 09/22] feat: added tooltip to 'forward to user' field --- apps/meteor/client/components/AutoCompleteAgent.tsx | 3 +++ .../client/components/Omnichannel/modals/ForwardChatModal.tsx | 1 + 2 files changed, 4 insertions(+) diff --git a/apps/meteor/client/components/AutoCompleteAgent.tsx b/apps/meteor/client/components/AutoCompleteAgent.tsx index 860ee3024ed8..f2cbebe46920 100644 --- a/apps/meteor/client/components/AutoCompleteAgent.tsx +++ b/apps/meteor/client/components/AutoCompleteAgent.tsx @@ -16,6 +16,7 @@ type AutoCompleteAgentProps = { excludeId?: string; showIdleAgents?: boolean; onlyAvailable?: boolean; + withTitle?: boolean; onChange: (value: string) => void; }; @@ -28,6 +29,7 @@ const AutoCompleteAgent = ({ excludeId, showIdleAgents = false, onlyAvailable = false, + withTitle = false, onChange, }: AutoCompleteAgentProps): ReactElement => { const [agentsFilter, setAgentsFilter] = useState(''); @@ -45,6 +47,7 @@ const AutoCompleteAgent = ({ return ( Date: Mon, 16 Oct 2023 10:46:14 -0300 Subject: [PATCH 10/22] chore: adjusted e2e tests --- .../Omnichannel/modals/ForwardChatModal.tsx | 4 ++-- .../omnichannel-transfer-to-another-agents.spec.ts | 2 ++ .../e2e/page-objects/fragments/home-content.ts | 14 +++----------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index 0327eac2e962..c7a581b14295 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -113,15 +113,15 @@ const ForwardChatModal = ({ {t('Forward_to_user')} { setValue('username', value); }} - value={getValues().username} /> diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts index 3c74065a9a84..39cea8cd18e8 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts @@ -65,6 +65,7 @@ test.describe('omnichannel-transfer-to-another-agent', () => { await agent2.poHomeOmnichannel.sidenav.switchStatus('offline'); await agent1.poHomeOmnichannel.content.btnForwardChat.click(); + await agent1.poHomeOmnichannel.content.inputModalAgentUserName.click(); await agent1.poHomeOmnichannel.content.inputModalAgentUserName.type('user2'); await expect(agent1.page.locator('text=Empty')).toBeVisible(); @@ -76,6 +77,7 @@ test.describe('omnichannel-transfer-to-another-agent', () => { await agent1.poHomeOmnichannel.sidenav.getSidebarItemByName(newVisitor.name).click(); await agent1.poHomeOmnichannel.content.btnForwardChat.click(); + await agent1.poHomeOmnichannel.content.inputModalAgentUserName.click(); await agent1.poHomeOmnichannel.content.inputModalAgentUserName.type('user2'); await agent1.page.locator('.rcx-option .rcx-option__wrapper >> text="user2"').click(); await agent1.poHomeOmnichannel.content.inputModalAgentForwardComment.type('any_comment'); diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index cb8c8b089095..ebbbb488bd38 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -197,7 +197,7 @@ export class HomeContent { } get inputModalAgentUserName(): Locator { - return this.page.locator('#modal-root input:nth-child(1)'); + return this.page.locator('#modal-root input[placeholder="Username"]'); } get inputModalAgentForwardComment(): Locator { @@ -237,16 +237,8 @@ export class HomeContent { async openLastThreadMessageMenu(): Promise { await this.page.locator('//main//aside >> [data-qa-type="message"]').last().hover(); - await this.page - .locator('//main//aside >> [data-qa-type="message"]') - .last() - .locator('role=button[name="More"]') - .waitFor(); - await this.page - .locator('//main//aside >> [data-qa-type="message"]') - .last() - .locator('role=button[name="More"]') - .click(); + await this.page.locator('//main//aside >> [data-qa-type="message"]').last().locator('role=button[name="More"]').waitFor(); + await this.page.locator('//main//aside >> [data-qa-type="message"]').last().locator('role=button[name="More"]').click(); } async toggleAlsoSendThreadToChannel(isChecked: boolean): Promise { From 19e3452b3a0fcddb9376049748ff93588eae8202 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 17 Oct 2023 07:43:52 -0600 Subject: [PATCH 11/22] Update apps/meteor/app/livechat/server/api/lib/users.ts Co-authored-by: Murtaza Patrawala <34130764+murtaza98@users.noreply.github.com> --- apps/meteor/app/livechat/server/api/lib/users.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/meteor/app/livechat/server/api/lib/users.ts b/apps/meteor/app/livechat/server/api/lib/users.ts index 2fc947f9ca45..66d9d091c262 100644 --- a/apps/meteor/app/livechat/server/api/lib/users.ts +++ b/apps/meteor/app/livechat/server/api/lib/users.ts @@ -46,6 +46,8 @@ async function findUsers({ if (!showIdleAgents) { const oldOr = query.$or; + const newOr = [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }]; + if (oldOr) { delete query.$or; Object.assign(query, { @@ -54,13 +56,13 @@ async function findUsers({ $or: oldOr, }, { - $or: [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }], + $or: newOr, }, ], }); } else { Object.assign(query, { - $or: [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }], + $or: newOr, }); } } From 892efe5608511edd4c0c8f45f13c6f6e87a3c43f Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Thu, 19 Oct 2023 14:25:43 -0300 Subject: [PATCH 12/22] feat: added tooltip to department field on agent edit page --- apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx index aa5a917405b4..4d87b4a62e9d 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx @@ -154,6 +154,7 @@ const AgentEdit: FC = ({ data, userDepartments, availableDepartm {t('Departments')} Date: Fri, 20 Oct 2023 11:53:39 -0300 Subject: [PATCH 13/22] feat: changed display name formatting --- .../meteor/client/components/Omnichannel/hooks/useAgentsList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts b/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts index 2f0949c5c223..e2f6f80f2355 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useAgentsList.ts @@ -50,7 +50,7 @@ export const useAgentsList = ( const items = agents.map((agent) => { const agentOption = { _updatedAt: new Date(agent._updatedAt), - label: agent.username || agent._id, + label: `${agent.name || agent._id} (@${agent.username})`, value: agent._id, _id: agent._id, }; From 545c091085a62a77d206e1c0f68aeef8162b5cf7 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 20 Oct 2023 12:16:38 -0300 Subject: [PATCH 14/22] feat: improved placeholder --- .../client/components/Omnichannel/modals/ForwardChatModal.tsx | 2 +- apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 3 ++- apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx index c7a581b14295..a4d095fdb90d 100644 --- a/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx +++ b/apps/meteor/client/components/Omnichannel/modals/ForwardChatModal.tsx @@ -118,7 +118,7 @@ const ForwardChatModal = ({ value={getValues().username} excludeId={room.servedBy?._id} showIdleAgents={idleAgentsAllowedForForwarding} - placeholder={t('Username')} + placeholder={t('Username_name_email')} onChange={(value) => { setValue('username', value); }} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index 46805a1f0e3e..f8e8c8ba835f 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -5453,6 +5453,7 @@ "Username_title": "Register username", "Username_has_been_updated": "Username has been updated", "Username_wants_to_start_otr_Do_you_want_to_accept": "{{username}} wants to start OTR. Do you want to accept?", + "Username_name_email": "Username, name or e-mail", "Users": "Users", "Users must use Two Factor Authentication": "Users must use Two Factor Authentication", "Users_added": "The users have been added", @@ -6091,4 +6092,4 @@ "unread_messages_counter_plural": "{{count}} unread messages", "Premium": "Premium", "Premium_capability": "Premium capability" -} \ No newline at end of file +} diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index 8da04e7c4e67..b3aa28f89fb8 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -4575,6 +4575,7 @@ "Username_Placeholder": "Digite os nomes de usuário...", "Username_title": "Cadastre um nome de usuário", "Username_wants_to_start_otr_Do_you_want_to_accept": "{{username}} quer começar OTR. Você aceita?", + "Username_name_email": "Nome de usuário, nome ou e-mail", "Users": "Usuários", "Users must use Two Factor Authentication": "Os usuários devem usar autenticação de dois fatores", "Users_added": "Os usuários foram adicionados", From 3f4db55c3b47e5f380ddc9b4569e35929c49a193 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 20 Oct 2023 14:28:11 -0300 Subject: [PATCH 15/22] chore: adjusting expressions file --- apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index f8e8c8ba835f..d260037d2aab 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -6092,4 +6092,4 @@ "unread_messages_counter_plural": "{{count}} unread messages", "Premium": "Premium", "Premium_capability": "Premium capability" -} +} \ No newline at end of file From e8f30cb75746a74082bb56aef04eeb018d9e867b Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 20 Oct 2023 15:15:24 -0300 Subject: [PATCH 16/22] Revert "feat: added tooltip to department field on agent edit page" This reverts commit 892efe5608511edd4c0c8f45f13c6f6e87a3c43f. --- apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx index 4d87b4a62e9d..aa5a917405b4 100644 --- a/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx +++ b/apps/meteor/client/views/omnichannel/agents/AgentEdit.tsx @@ -154,7 +154,6 @@ const AgentEdit: FC = ({ data, userDepartments, availableDepartm {t('Departments')} Date: Fri, 20 Oct 2023 13:16:25 -0600 Subject: [PATCH 17/22] cr --- .../app/livechat/server/api/lib/users.ts | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/apps/meteor/app/livechat/server/api/lib/users.ts b/apps/meteor/app/livechat/server/api/lib/users.ts index 66d9d091c262..49ac5682a6c0 100644 --- a/apps/meteor/app/livechat/server/api/lib/users.ts +++ b/apps/meteor/app/livechat/server/api/lib/users.ts @@ -24,47 +24,26 @@ async function findUsers({ pagination: { offset: number; count: number; sort: any }; }): Promise<{ users: ILivechatAgent[]; count: number; offset: number; total: number }> { const query: FilterOperators = {}; + const orConditions: FilterOperators['$or'] = []; if (text) { const filterReg = new RegExp(escapeRegExp(text), 'i'); - Object.assign(query, { - $or: [{ username: filterReg }, { name: filterReg }, { 'emails.address': filterReg }], - }); + orConditions.push({ $or: [{ username: filterReg }, { name: filterReg }, { 'emails.address': filterReg }] }); } if (onlyAvailable) { - Object.assign(query, { - statusLivechat: 'available', - }); + query.statusLivechat = 'available'; } if (excludeId) { - Object.assign(query, { - _id: { $ne: excludeId }, - }); + query._id = { $ne: excludeId }; } if (!showIdleAgents) { - const oldOr = query.$or; - - const newOr = [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }]; + orConditions.push({ $or: [{ status: { $exists: true, $ne: 'offline' }, roles: { $ne: 'bot' } }, { roles: 'bot' }] }); + } - if (oldOr) { - delete query.$or; - Object.assign(query, { - $and: [ - { - $or: oldOr, - }, - { - $or: newOr, - }, - ], - }); - } else { - Object.assign(query, { - $or: newOr, - }); - } + if (orConditions.length) { + query.$and = orConditions; } const [ From 971fd496379cd323bed2bca1b508990505b12659 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 20 Oct 2023 13:43:53 -0600 Subject: [PATCH 18/22] Update rotten-dryers-allow.md --- .changeset/rotten-dryers-allow.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/rotten-dryers-allow.md b/.changeset/rotten-dryers-allow.md index 5bb7d69d3408..e200f2937798 100644 --- a/.changeset/rotten-dryers-allow.md +++ b/.changeset/rotten-dryers-allow.md @@ -3,3 +3,4 @@ --- chore: Use `users/agent` endpoint in forwarding modals +feat: Add pagination & tooltips to agent's dropdown on forwarding modal From 9bb4388ad65948c14f60446a29c16999cdbf82bc Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Fri, 20 Oct 2023 14:09:03 -0600 Subject: [PATCH 19/22] Update rotten-dryers-allow.md --- .changeset/rotten-dryers-allow.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/rotten-dryers-allow.md b/.changeset/rotten-dryers-allow.md index e200f2937798..db044864a017 100644 --- a/.changeset/rotten-dryers-allow.md +++ b/.changeset/rotten-dryers-allow.md @@ -2,5 +2,4 @@ "@rocket.chat/meteor": patch --- -chore: Use `users/agent` endpoint in forwarding modals feat: Add pagination & tooltips to agent's dropdown on forwarding modal From ff7e37712d3d7c3936548aec3eaa2867da8036d5 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Fri, 20 Oct 2023 17:09:57 -0300 Subject: [PATCH 20/22] Update .changeset/rotten-dryers-allow.md --- .changeset/rotten-dryers-allow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rotten-dryers-allow.md b/.changeset/rotten-dryers-allow.md index db044864a017..154dea572780 100644 --- a/.changeset/rotten-dryers-allow.md +++ b/.changeset/rotten-dryers-allow.md @@ -2,4 +2,4 @@ "@rocket.chat/meteor": patch --- -feat: Add pagination & tooltips to agent's dropdown on forwarding modal +Add pagination & tooltips to agent's dropdown on forwarding modal From 99d412d0a5843ca22794f575a4e2679c85102d12 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 20 Oct 2023 17:51:26 -0300 Subject: [PATCH 21/22] chore: adjusted e2e tests --- apps/meteor/tests/e2e/page-objects/fragments/home-content.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index ebbbb488bd38..2ba8cd6428d9 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -197,7 +197,7 @@ export class HomeContent { } get inputModalAgentUserName(): Locator { - return this.page.locator('#modal-root input[placeholder="Username"]'); + return this.page.locator('#modal-root input[placeholder="Username, name or e-mail"]'); } get inputModalAgentForwardComment(): Locator { From 70983ef4654d1619c22282447cf65d7c55357ce4 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Fri, 20 Oct 2023 19:48:56 -0300 Subject: [PATCH 22/22] chore: fixed e2e tests --- .../omnichannel/omnichannel-transfer-to-another-agents.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts index 39cea8cd18e8..dc31f54be934 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-transfer-to-another-agents.spec.ts @@ -79,7 +79,7 @@ test.describe('omnichannel-transfer-to-another-agent', () => { await agent1.poHomeOmnichannel.content.btnForwardChat.click(); await agent1.poHomeOmnichannel.content.inputModalAgentUserName.click(); await agent1.poHomeOmnichannel.content.inputModalAgentUserName.type('user2'); - await agent1.page.locator('.rcx-option .rcx-option__wrapper >> text="user2"').click(); + await agent1.page.locator('.rcx-option .rcx-option__wrapper >> text="user2 (@user2)"').click(); await agent1.poHomeOmnichannel.content.inputModalAgentForwardComment.type('any_comment'); await agent1.poHomeOmnichannel.content.btnModalConfirm.click(); await expect(agent1.poHomeOmnichannel.toastSuccess).toBeVisible();