From b508882826d8c4cdfb284fb22dad2e8e2ad9db96 Mon Sep 17 00:00:00 2001 From: Aleksander Nicacio da Silva Date: Wed, 19 Jul 2023 11:22:06 -0300 Subject: [PATCH 1/3] fix: Omnichannel tag id's being stored instead of tag labels --- .../client/components/Omnichannel/Tags.tsx | 8 ++++--- .../Omnichannel/hooks/useLivechatTags.ts | 4 +++- .../calls/contextualBar/VoipInfo.tsx | 4 +--- .../omnichannel/directory/chats/ChatTable.tsx | 7 ++---- .../directory/chats/contextualBar/ChatInfo.js | 4 +--- .../chats/contextualBar/ChatInfoDirectory.js | 4 +--- .../directory/chats/hooks/useTagsLabels.tsx | 24 ------------------- apps/meteor/ee/client/hooks/useTagsList.ts | 2 +- 8 files changed, 14 insertions(+), 43 deletions(-) delete mode 100644 apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx diff --git a/apps/meteor/client/components/Omnichannel/Tags.tsx b/apps/meteor/client/components/Omnichannel/Tags.tsx index 19a8dba8d36fe..c0edcd96164b6 100644 --- a/apps/meteor/client/components/Omnichannel/Tags.tsx +++ b/apps/meteor/client/components/Omnichannel/Tags.tsx @@ -27,10 +27,11 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) const { data: tagsResult, isInitialLoading } = useLivechatTags({ department, + viewAll: !department, }); const customTags = useMemo(() => { - return tags.filter((tag) => !tagsResult?.tags.find((rtag) => rtag._id === tag)); + return tags.filter((tag) => !tagsResult?.tags.find((rtag) => rtag.name === tag)); }, [tags, tagsResult?.tags]); const dispatchToastMessage = useToastMessageDispatch(); @@ -56,7 +57,7 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) return; } - if (tags.some((tag) => tag === tagValue)) { + if (tags.includes(tagValue)) { dispatchToastMessage({ type: 'error', message: t('Tag_already_exists') }); return; } @@ -79,9 +80,10 @@ const Tags = ({ tags = [], handler, error, tagRequired, department }: TagsProps) { - handler(tags.map((tag) => tag.value)); + handler(tags.map((tag) => tag.label)); }} department={department} + viewAll={!department} /> ) : ( diff --git a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts index ac042274a2c74..4bd85be403426 100644 --- a/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts +++ b/apps/meteor/client/components/Omnichannel/hooks/useLivechatTags.ts @@ -4,16 +4,18 @@ import { useQuery } from '@tanstack/react-query'; type Props = { department?: string; text?: string; + viewAll?: boolean; }; export const useLivechatTags = (options: Props) => { const getTags = useEndpoint('GET', '/v1/livechat/tags'); - const { department, text } = options; + const { department, text, viewAll } = options; return useQuery(['/v1/livechat/tags', text, department], () => getTags({ text: text || '', ...(department && { department }), + viewAll: viewAll ? 'true' : 'false', }), ); }; diff --git a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx index 04731c35b1cfd..f76bda4c0832b 100644 --- a/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx +++ b/apps/meteor/client/views/omnichannel/directory/calls/contextualBar/VoipInfo.tsx @@ -19,7 +19,6 @@ import { UserStatus } from '../../../../../components/UserStatus'; import UserAvatar from '../../../../../components/avatar/UserAvatar'; import { useIsCallReady } from '../../../../../contexts/CallContext'; import AgentInfoDetails from '../../../components/AgentInfoDetails'; -import { useTagsLabels } from '../../chats/hooks/useTagsLabels'; import AgentField from '../../components/AgentField'; import { InfoField } from './InfoField'; import { VoipInfoCallButton } from './VoipInfoCallButton'; @@ -43,7 +42,6 @@ export const VoipInfo = ({ room, onClickClose /* , onClickReport */ }: VoipInfo const shouldShowWrapup = useMemo(() => lastMessage?.t === 'voip-call-wrapup' && lastMessage?.msg, [lastMessage]); const shouldShowTags = useMemo(() => tags && tags.length > 0, [tags]); const _name = fname || name; - const getTagLabel = useTagsLabels(); return ( <> @@ -85,7 +83,7 @@ export const VoipInfo = ({ room, onClickClose /* , onClickReport */ }: VoipInfo {tags?.map((tag: string) => ( - {getTagLabel(tag)} + {tag} ))} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx b/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx index 5325a62601d9a..50c1651f69ff8 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx +++ b/apps/meteor/client/views/omnichannel/directory/chats/ChatTable.tsx @@ -18,7 +18,6 @@ import { import { usePagination } from '../../../../components/GenericTable/hooks/usePagination'; import { useSort } from '../../../../components/GenericTable/hooks/useSort'; import { useCurrentChats } from '../../currentChats/hooks/useCurrentChats'; -import { useTagsLabels } from './hooks/useTagsLabels'; const ChatTable = () => { const t = useTranslation(); @@ -29,8 +28,6 @@ const ChatTable = () => { const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination(); const { sortBy, sortDirection, setSort } = useSort<'fname' | 'department' | 'ts' | 'chatDuration' | 'closedAt'>('fname'); - const getTagLabel = useTagsLabels(); - const query = useMemo( () => ({ sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`, @@ -114,7 +111,7 @@ const ChatTable = () => { mie='x4' > - {getTagLabel(tag)} + {tag} ))} @@ -128,7 +125,7 @@ const ChatTable = () => { {moment(closedAt).format('L LTS')} ), - [getTagLabel, onRowClick], + [onRowClick], ); return ( diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js index 2c5c4571271ec..7c32188e7e343 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfo.js @@ -16,7 +16,6 @@ import Label from '../../../components/Label'; import { AgentField, SlaField, ContactField, SourceField } from '../../components'; import PriorityField from '../../components/PriorityField'; import { useOmnichannelRoomInfo } from '../../hooks/useOmnichannelRoomInfo'; -import { useTagsLabels } from '../hooks/useTagsLabels'; import DepartmentField from './DepartmentField'; import VisitorClientInfo from './VisitorClientInfo'; @@ -50,7 +49,6 @@ function ChatInfo({ id, route }) { queuedAt, } = room || { room: { v: {} } }; - const getTagLabel = useTagsLabels(!!tags?.length); const routePath = useRoute(route || 'omnichannel-directory'); const canViewCustomFields = usePermission('view-livechat-room-customfields'); const subscription = useUserSubscription(id); @@ -110,7 +108,7 @@ function ChatInfo({ id, route }) { {tags.map((tag) => ( - {getTagLabel(tag)} + {tag} ))} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js index 3985304428bd3..ab862dd3fb2df 100644 --- a/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js +++ b/apps/meteor/client/views/omnichannel/directory/chats/contextualBar/ChatInfoDirectory.js @@ -16,7 +16,6 @@ import Info from '../../../components/Info'; import Label from '../../../components/Label'; import { AgentField, ContactField, SlaField } from '../../components'; import PriorityField from '../../components/PriorityField'; -import { useTagsLabels } from '../hooks/useTagsLabels'; import DepartmentField from './DepartmentField'; import VisitorClientInfo from './VisitorClientInfo'; @@ -45,7 +44,6 @@ function ChatInfoDirectory({ id, route = undefined, room }) { queuedAt, } = room || { room: { v: {} } }; - const getTagLabel = useTagsLabels(); const routePath = useRoute(route || 'omnichannel-directory'); const canViewCustomFields = () => hasPermission('view-livechat-room-customfields'); const subscription = useUserSubscription(id); @@ -106,7 +104,7 @@ function ChatInfoDirectory({ id, route = undefined, room }) { {tags.map((tag) => ( - {getTagLabel(tag)} + {tag} ))} diff --git a/apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx b/apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx deleted file mode 100644 index 1790359e46fb7..0000000000000 --- a/apps/meteor/client/views/omnichannel/directory/chats/hooks/useTagsLabels.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useEndpoint } from '@rocket.chat/ui-contexts'; -import { useQuery } from '@tanstack/react-query'; -import { useCallback, useMemo } from 'react'; - -// TODO: Remove this hook when the endpoint is changed -// to return labels instead of tag id's -export const useTagsLabels = (enabled = true) => { - const getTags = useEndpoint('GET', '/v1/livechat/tags'); - const { data: tagsData, isInitialLoading } = useQuery(['/v1/livechat/tags'], () => getTags({ text: '', viewAll: 'true' }), { - enabled, - }); - - const labels = useMemo(() => { - const { tags = [] } = tagsData || {}; - return tags.reduce>((acc, tag) => ({ ...acc, [tag._id]: tag.name }), {}); - }, [tagsData]); - - return useCallback( - (tagId: string) => { - return isInitialLoading ? tagId : labels[tagId] || tagId; - }, - [isInitialLoading, labels], - ); -}; diff --git a/apps/meteor/ee/client/hooks/useTagsList.ts b/apps/meteor/ee/client/hooks/useTagsList.ts index 78dfc4de00937..467d92103ea2b 100644 --- a/apps/meteor/ee/client/hooks/useTagsList.ts +++ b/apps/meteor/ee/client/hooks/useTagsList.ts @@ -44,7 +44,7 @@ export const useTagsList = (options: TagsListOptions): UseTagsListResult => { items: tags.map((tag: any) => ({ _id: tag._id, label: tag.name, - value: tag._id, + value: tag.name, _updatedAt: new Date(tag._updatedAt), })), itemCount: total, From 5a4b1e9cfc6e33012e2951ed7fdd3575ffac1173 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 19 Jul 2023 08:37:29 -0600 Subject: [PATCH 2/3] rollback tags ids --- .../server/lib/canned-responses.js | 9 +++-- .../ee/app/api-enterprise/server/lib/tags.ts | 33 ------------------- 2 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 apps/meteor/ee/app/api-enterprise/server/lib/tags.ts diff --git a/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js b/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js index 1a7d478b6c3bf..15efea01d8aa8 100644 --- a/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js +++ b/apps/meteor/ee/app/api-enterprise/server/lib/canned-responses.js @@ -2,7 +2,6 @@ import { escapeRegExp } from '@rocket.chat/string-helpers'; import { CannedResponse } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission'; -import { getTagsInformation } from './tags'; import { getDepartmentsWhichUserCanAccess } from '../../../livechat-enterprise/server/api/lib/departments'; export async function findAllCannedResponses({ userId }) { @@ -22,7 +21,7 @@ export async function findAllCannedResponses({ userId }) { }, ], }).toArray(); - return getTagsInformation(cannedResponses); + return cannedResponses; } // If the user it not any of the previous roles nor an agent, then get only his own responses @@ -31,7 +30,7 @@ export async function findAllCannedResponses({ userId }) { scope: 'user', userId, }).toArray(); - return getTagsInformation(cannedResponses); + return cannedResponses; } // Last scenario: user is an agent, so get his own responses and those from the departments he is in @@ -55,7 +54,7 @@ export async function findAllCannedResponses({ userId }) { ], }).toArray(); - return getTagsInformation(cannedResponses); + return cannedResponses; } export async function findAllCannedResponsesFilter({ userId, shortcut, text, departmentId, scope, createdBy, tags = [], options = {} }) { @@ -129,7 +128,7 @@ export async function findAllCannedResponsesFilter({ userId, shortcut, text, dep }); const [cannedResponses, total] = await Promise.all([cursor.toArray(), totalCount]); return { - cannedResponses: await getTagsInformation(cannedResponses), + cannedResponses, total, }; } diff --git a/apps/meteor/ee/app/api-enterprise/server/lib/tags.ts b/apps/meteor/ee/app/api-enterprise/server/lib/tags.ts deleted file mode 100644 index c4ffc97da7f84..0000000000000 --- a/apps/meteor/ee/app/api-enterprise/server/lib/tags.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { ILivechatTag, IOmnichannelCannedResponse } from '@rocket.chat/core-typings'; -import { LivechatTag } from '@rocket.chat/models'; - -const filterTags = (tags: string[], serverTags: ILivechatTag[]) => { - return tags.reduce((acc, tag) => { - const found = serverTags.find((serverTag) => serverTag._id === tag); - if (found) { - acc.push(found.name); - } else { - acc.push(tag); - } - return acc; - }, [] as string[]); -}; - -export const getTagsInformation = async (cannedResponses: IOmnichannelCannedResponse[]) => { - return Promise.all( - cannedResponses.map(async (cannedResponse) => { - const { tags } = cannedResponse; - - if (!Array.isArray(tags) || !tags.length) { - return cannedResponse; - } - - const serverTags = await LivechatTag.findInIds(tags, { projection: { _id: 1, name: 1 } }).toArray(); - - // Known limitation: if a tag was added and removed before this, it will return the tag id instead of the name - cannedResponse.tags = filterTags(tags, serverTags); - - return cannedResponse; - }), - ); -}; From 92119223819d1abeec4b66c24d98746375c8fe43 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Wed, 19 Jul 2023 13:15:02 -0600 Subject: [PATCH 3/3] oops --- .../livechat-enterprise/server/hooks/afterTagRemoved.ts | 4 ++-- .../livechat-enterprise/server/lib/LivechatEnterprise.ts | 2 +- .../tests/end-to-end/api/livechat/15-canned-responses.ts | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts index 451ce972f62ae..6066647c79a44 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/afterTagRemoved.ts @@ -5,9 +5,9 @@ import { callbacks } from '../../../../../lib/callbacks'; callbacks.add( 'livechat.afterTagRemoved', async (tag) => { - const { _id } = tag; + const { name } = tag; - await CannedResponse.removeTagFromCannedResponses(_id); + await CannedResponse.removeTagFromCannedResponses(name); }, callbacks.priority.MEDIUM, 'on-tag-removed-remove-references', diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts index 1471ef8d40e13..d94ad93c16f04 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.ts @@ -129,7 +129,7 @@ export const LivechatEnterprise = { async removeTag(_id: string) { check(_id, String); - const tag = await LivechatTag.findOneById(_id, { projection: { _id: 1 } }); + const tag = await LivechatTag.findOneById(_id, { projection: { _id: 1, name: 1 } }); if (!tag) { throw new Meteor.Error('tag-not-found', 'Tag not found', { method: 'livechat:removeTag' }); diff --git a/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts b/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts index 82b6eb6feb214..d63214d9528b5 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts @@ -136,12 +136,12 @@ import { removeTag, saveTags } from '../../../data/livechat/tags'; const { body } = await request .post(api('canned-responses')) .set(credentials) - .send({ shortcut: 'shortcutxxx', scope: 'user', tags: [tag._id], text: 'text' }) + .send({ shortcut: 'shortcutxxx', scope: 'user', tags: [tag.name], text: 'text' }) .expect(200); expect(body).to.have.property('success', true); - const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag._id }).expect(200); + const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag.name }).expect(200); expect(getResult).to.have.property('success', true); expect(getResult.cannedResponses).to.be.an('array').with.lengthOf(1); @@ -154,14 +154,14 @@ import { removeTag, saveTags } from '../../../data/livechat/tags'; const { body } = await request .post(api('canned-responses')) .set(credentials) - .send({ shortcut: 'shortcutxxxx', scope: 'user', tags: [tag._id], text: 'text' }) + .send({ shortcut: 'shortcutxxxx', scope: 'user', tags: [tag.name], text: 'text' }) .expect(200); expect(body).to.have.property('success', true); await removeTag(tag._id); - const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag._id }).expect(200); + const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag.name }).expect(200); expect(getResult).to.have.property('success', true); expect(getResult.cannedResponses).to.be.an('array').with.lengthOf(0);