From f0eca521e05c0b1b8ec079002b78fcc185b0deb4 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Thu, 17 Jun 2021 23:38:57 -0300 Subject: [PATCH] [IMPROVE] Paginated multiselect for EE tags (#22315) * [IMPROVE] Paginated multiselect for EE tags * add missing tag interface * rename ILivechatTag interface * Add type to endpoint Co-authored-by: Rafael Ferreira --- client/components/Omnichannel/Tags.js | 9 ++- client/contexts/ServerContext/endpoints.ts | 2 + .../endpoints/v1/livechat/tagsList.ts | 13 +++++ .../omnichannel/currentChats/FilterByText.js | 2 +- definition/ILivechatTag.ts | 7 +++ definition/ILivechatTagRecord.ts | 10 ++++ ee/client/hooks/useTagsList.ts | 58 +++++++++++++++++++ ee/client/omnichannel/tags/CurrentChatTags.js | 37 +++++++++--- 8 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 client/contexts/ServerContext/endpoints/v1/livechat/tagsList.ts create mode 100644 definition/ILivechatTag.ts create mode 100644 definition/ILivechatTagRecord.ts create mode 100644 ee/client/hooks/useTagsList.ts diff --git a/client/components/Omnichannel/Tags.js b/client/components/Omnichannel/Tags.js index 7372776a4832f..015fbce60776f 100644 --- a/client/components/Omnichannel/Tags.js +++ b/client/components/Omnichannel/Tags.js @@ -21,6 +21,7 @@ const Tags = ({ tags = [], handler = () => {}, error = '' }) => { const dispatchToastMessage = useToastMessageDispatch(); const [tagValue, handleTagValue] = useState(''); + const [paginatedTagValue, handlePaginatedTagValue] = useState([]); const removeTag = (tag) => { const tagsFiltered = tags.filter((tagArray) => tagArray !== tag); @@ -52,7 +53,13 @@ const Tags = ({ tags = [], handler = () => {}, error = '' }) => { {t('Tags')} {Tags && tagsList && tagsList.length > 0 ? ( - + { + handler(tags.map((tag) => tag.label)); + handlePaginatedTagValue(tags); + }} + /> ) : ( <> diff --git a/client/contexts/ServerContext/endpoints.ts b/client/contexts/ServerContext/endpoints.ts index 6e1d47204534b..faec94266142e 100644 --- a/client/contexts/ServerContext/endpoints.ts +++ b/client/contexts/ServerContext/endpoints.ts @@ -15,6 +15,7 @@ import { LivechatDepartment } from './endpoints/v1/livechat/department'; import { LivechatDepartmentsByUnit } from './endpoints/v1/livechat/departmentsByUnit'; import { LivechatMonitorsList } from './endpoints/v1/livechat/monitorsList'; import { LivechatRoomOnHoldEndpoint } from './endpoints/v1/livechat/onHold'; +import { LivechatTagsList } from './endpoints/v1/livechat/tagsList'; import { LivechatVisitorInfoEndpoint } from './endpoints/v1/livechat/visitorInfo'; import { AutocompleteAvailableForTeamsEndpoint as RoomsAutocompleteTeamsEndpoint } from './endpoints/v1/rooms/autocompleteAvailableForTeams'; import { AutocompleteChannelAndPrivateEndpoint as RoomsAutocompleteEndpoint } from './endpoints/v1/rooms/autocompleteChannelAndPrivate'; @@ -44,6 +45,7 @@ export type ServerEndpoints = { 'livechat/visitors.info': LivechatVisitorInfoEndpoint; 'livechat/room.onHold': LivechatRoomOnHoldEndpoint; 'livechat/monitors.list': LivechatMonitorsList; + 'livechat/tags.list': LivechatTagsList; 'livechat/department': LivechatDepartment; 'livechat/departments.by-unit/': LivechatDepartmentsByUnit; }; diff --git a/client/contexts/ServerContext/endpoints/v1/livechat/tagsList.ts b/client/contexts/ServerContext/endpoints/v1/livechat/tagsList.ts new file mode 100644 index 0000000000000..21a1c7a80eb0c --- /dev/null +++ b/client/contexts/ServerContext/endpoints/v1/livechat/tagsList.ts @@ -0,0 +1,13 @@ +import { ILivechatTag } from '../../../../../../definition/ILivechatTag'; +import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi'; + +export type LivechatTagsList = { + GET: (params: { + text: string; + offset: number; + count: number; + }) => { + tags: ObjectFromApi[]; + total: number; + }; +}; diff --git a/client/views/omnichannel/currentChats/FilterByText.js b/client/views/omnichannel/currentChats/FilterByText.js index fe2b4f0f25905..d66bd4b4b6dd5 100644 --- a/client/views/omnichannel/currentChats/FilterByText.js +++ b/client/views/omnichannel/currentChats/FilterByText.js @@ -84,7 +84,7 @@ const FilterByText = ({ setFilter, reload, ...props }) => { ...(department?.value && { department: department.value }), from: from && moment(new Date(from)).utc().format('YYYY-MM-DDTHH:mm:ss'), to: to && moment(new Date(to)).utc().format('YYYY-MM-DDTHH:mm:ss'), - tags, + tags: tags.map((tag) => tag.label), customFields: customFields.reduce(reducer, {}), }); }, [setFilter, guest, servedBy, status, department, from, to, tags, customFields]); diff --git a/definition/ILivechatTag.ts b/definition/ILivechatTag.ts new file mode 100644 index 0000000000000..196c74c8dfd29 --- /dev/null +++ b/definition/ILivechatTag.ts @@ -0,0 +1,7 @@ +export interface ILivechatTag { + _id: string; + name: string; + description: string; + numDepartments: number; + departments: Array; +} diff --git a/definition/ILivechatTagRecord.ts b/definition/ILivechatTagRecord.ts new file mode 100644 index 0000000000000..35456ce6a36d7 --- /dev/null +++ b/definition/ILivechatTagRecord.ts @@ -0,0 +1,10 @@ +import { IRocketChatRecord } from './IRocketChatRecord'; + + +export interface ILivechatTagRecord extends IRocketChatRecord { + _id: string; + name: string; + description: string; + numDepartments: number; + departments: Array; +} diff --git a/ee/client/hooks/useTagsList.ts b/ee/client/hooks/useTagsList.ts new file mode 100644 index 0000000000000..25e147c097451 --- /dev/null +++ b/ee/client/hooks/useTagsList.ts @@ -0,0 +1,58 @@ +import { useCallback, useState } from 'react'; + +import { useEndpoint } from '../../../client/contexts/ServerContext'; +import { useScrollableRecordList } from '../../../client/hooks/lists/useScrollableRecordList'; +import { useComponentDidUpdate } from '../../../client/hooks/useComponentDidUpdate'; +import { RecordList } from '../../../client/lib/lists/RecordList'; +import { ILivechatTagRecord } from '../../../definition/ILivechatTagRecord'; + +type TagsListOptions = { + filter: string; +}; + +export const useTagsList = ( + options: TagsListOptions, +): { + itemsList: RecordList; + initialItemCount: number; + reload: () => void; + loadMoreItems: (start: number, end: number) => void; +} => { + const [itemsList, setItemsList] = useState(() => new RecordList()); + const reload = useCallback(() => setItemsList(new RecordList()), []); + + const getTags = useEndpoint('GET', 'livechat/tags.list'); + + useComponentDidUpdate(() => { + options && reload(); + }, [options, reload]); + + const fetchData = useCallback( + async (start, end) => { + const { tags, total } = await getTags({ + text: options.filter, + offset: start, + count: end + start, + }); + return { + items: tags.map((tag: any) => { + tag._updatedAt = new Date(tag._updatedAt); + tag.label = tag.name; + tag.value = { value: tag._id, label: tag.name }; + return tag; + }), + itemCount: total, + }; + }, + [getTags, options.filter], + ); + + const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25); + + return { + reload, + itemsList, + loadMoreItems, + initialItemCount, + }; +}; diff --git a/ee/client/omnichannel/tags/CurrentChatTags.js b/ee/client/omnichannel/tags/CurrentChatTags.js index 597abddf9d08f..41af19e487676 100644 --- a/ee/client/omnichannel/tags/CurrentChatTags.js +++ b/ee/client/omnichannel/tags/CurrentChatTags.js @@ -1,16 +1,35 @@ -import { MultiSelect } from '@rocket.chat/fuselage'; -import React, { useMemo } from 'react'; +import { PaginatedMultiSelectFiltered } from '@rocket.chat/fuselage'; +import React, { useMemo, useState } from 'react'; -import { useEndpointData } from '../../../../client/hooks/useEndpointData'; +import { useRecordList } from '../../../../client/hooks/lists/useRecordList'; +import { AsyncStatePhase } from '../../../../client/hooks/useAsyncState'; +import { useTagsList } from '../../hooks/useTagsList'; -const CurrentChatTags = ({ value, handler, ...props }) => { - const { value: data } = useEndpointData('livechat/tags.list'); - const options = useMemo( - () => (data && data.tags ? data.tags.map(({ name }) => [name, name]) : []), - [data], +const CurrentChatTags = ({ value, handler }) => { + const [tagsFilter, setTagsFilter] = useState(''); + + const { itemsList: tagsList, loadMoreItems: loadMoreTags } = useTagsList( + useMemo(() => ({ filter: tagsFilter }), [tagsFilter]), ); - return ; + const { phase: tagsPhase, items: tagsItems, itemCount: tagsTotal } = useRecordList(tagsList); + + return ( + {} + : (start) => loadMoreTags(start, Math.min(50, tagsTotal)) + } + /> + ); }; export default CurrentChatTags;