Skip to content

Commit

Permalink
[IMPROVE] Paginated multiselect for EE tags (#22315)
Browse files Browse the repository at this point in the history
* [IMPROVE] Paginated multiselect for EE tags

* add missing tag interface

* rename ILivechatTag interface

* Add type to endpoint

Co-authored-by: Rafael Ferreira <rafaelblink@gmail.com>
  • Loading branch information
2 people authored and ggazzo committed Jun 24, 2021
1 parent 151b4ec commit f0eca52
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 11 deletions.
9 changes: 8 additions & 1 deletion client/components/Omnichannel/Tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -52,7 +53,13 @@ const Tags = ({ tags = [], handler = () => {}, error = '' }) => {
<Field.Label mb='x4'>{t('Tags')}</Field.Label>
{Tags && tagsList && tagsList.length > 0 ? (
<Field.Row>
<Tags value={tags} handler={handler} />
<Tags
value={paginatedTagValue}
handler={(tags) => {
handler(tags.map((tag) => tag.label));
handlePaginatedTagValue(tags);
}}
/>
</Field.Row>
) : (
<>
Expand Down
2 changes: 2 additions & 0 deletions client/contexts/ServerContext/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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;
};
Expand Down
13 changes: 13 additions & 0 deletions client/contexts/ServerContext/endpoints/v1/livechat/tagsList.ts
Original file line number Diff line number Diff line change
@@ -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<ILivechatTag>[];
total: number;
};
};
2 changes: 1 addition & 1 deletion client/views/omnichannel/currentChats/FilterByText.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
7 changes: 7 additions & 0 deletions definition/ILivechatTag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface ILivechatTag {
_id: string;
name: string;
description: string;
numDepartments: number;
departments: Array<string>;
}
10 changes: 10 additions & 0 deletions definition/ILivechatTagRecord.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IRocketChatRecord } from './IRocketChatRecord';


export interface ILivechatTagRecord extends IRocketChatRecord {
_id: string;
name: string;
description: string;
numDepartments: number;
departments: Array<string>;
}
58 changes: 58 additions & 0 deletions ee/client/hooks/useTagsList.ts
Original file line number Diff line number Diff line change
@@ -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<ILivechatTagRecord>;
initialItemCount: number;
reload: () => void;
loadMoreItems: (start: number, end: number) => void;
} => {
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatTagRecord>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatTagRecord>()), []);

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,
};
};
37 changes: 28 additions & 9 deletions ee/client/omnichannel/tags/CurrentChatTags.js
Original file line number Diff line number Diff line change
@@ -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 <MultiSelect options={options} value={value} onChange={handler} flexGrow={1} {...props} />;
const { phase: tagsPhase, items: tagsItems, itemCount: tagsTotal } = useRecordList(tagsList);

return (
<PaginatedMultiSelectFiltered
maxWidth={'100%'}
flexGrow={1}
filter={tagsFilter}
setFilter={setTagsFilter}
onChange={handler}
options={tagsItems}
value={value}
endReached={
tagsPhase === AsyncStatePhase.LOADING
? () => {}
: (start) => loadMoreTags(start, Math.min(50, tagsTotal))
}
/>
);
};

export default CurrentChatTags;

0 comments on commit f0eca52

Please sign in to comment.