Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
key: ${{ runner.OS }}-node_modules-2-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}

- name: Cache meteor local
uses: actions/cache@v2
Expand Down Expand Up @@ -246,7 +246,7 @@ jobs:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
key: ${{ runner.OS }}-node_modules-2-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}

- name: NPM install
if: steps.cache-nodemodules.outputs.cache-hit != 'true' || steps.cache-cypress.outputs.cache-hit != 'true'
Expand Down Expand Up @@ -310,7 +310,7 @@ jobs:
path: |
./node_modules
./ee/server/services/node_modules
key: ${{ runner.OS }}-node_modules-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}
key: ${{ runner.OS }}-node_modules-2-${{ hashFiles('**/package-lock.json', '.github/workflows/build_and_test.yml') }}

- name: Cache meteor local
uses: actions/cache@v2
Expand Down
63 changes: 38 additions & 25 deletions client/components/AutoCompleteAgent.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,51 @@
import { AutoComplete, Option } from '@rocket.chat/fuselage';
import { PaginatedSelectFiltered } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import React, { memo, useMemo, useState } from 'react';

import { useTranslation } from '../contexts/TranslationContext';
import { useEndpointData } from '../hooks/useEndpointData';
import { useRecordList } from '../hooks/lists/useRecordList';
import { AsyncStatePhase } from '../lib/asyncState';
import { useAgentsList } from './Omnichannel/hooks/useAgentsList';

const AutoCompleteAgent = (props) => {
const t = useTranslation();
const [filter, setFilter] = useState('');
const { value: data } = useEndpointData(
'livechat/users/agent',
useMemo(() => ({ text: filter }), [filter]),
);
const [agentsFilter, setAgentsFilter] = useState('');

const debouncedAgentsFilter = useDebouncedValue(agentsFilter, 500);

const options = useMemo(
() => (data && [...data.users.map((user) => ({ value: user._id, label: user.name }))]) || [],
[data],
const { itemsList: AgentsList, loadMoreItems: loadMoreAgents } = useAgentsList(
useMemo(() => ({ text: debouncedAgentsFilter }), [debouncedAgentsFilter]),
);
const optionsWithAll = useMemo(
() =>
(data && [
{ value: 'all', label: t('All') },
...data.users.map((user) => ({ value: user._id, label: user.name })),
]) || [{ value: 'all', label: t('All') }],
[data, t],

const { phase: agentsPhase, items: agentsItems, itemCount: agentsTotal } = useRecordList(
AgentsList,
);

const sortedByName = agentsItems.sort((a, b) => {
if (a.value === 'all') {
return -1;
}

if (a.usename > b.usename) {
return 1;
}
if (a.usename < b.usename) {
return -1;
}

return 0;
});

return (
<AutoComplete
<PaginatedSelectFiltered
{...props}
filter={filter}
setFilter={setFilter}
renderSelected={({ label }) => <>{label}</>}
renderItem={({ value, ...props }) => <Option key={value} {...props} />}
options={props.empty ? options : optionsWithAll}
flexShrink={0}
filter={agentsFilter}
setFilter={setAgentsFilter}
options={sortedByName}
endReached={
agentsPhase === AsyncStatePhase.LOADING
? () => {}
: (start) => loadMoreAgents(start, Math.min(50, agentsTotal))
}
/>
);
};
Expand Down
10 changes: 7 additions & 3 deletions client/components/AutoCompleteDepartment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ import { AsyncStatePhase } from '../hooks/useAsyncState';
import { useDepartmentsList } from './Omnichannel/hooks/useDepartmentsList';

const AutoCompleteDepartment = (props) => {
const { value, onlyMyDepartments = false, onChange = () => {} } = props;
const { value, onlyMyDepartments = false, onChange = () => {}, haveAll = false } = props;

const t = useTranslation();
const [departmentsFilter, setDepartmentsFilter] = useState('');

const debouncedDepartmentsFilter = useDebouncedValue(departmentsFilter, 500);

const { itemsList: departmentsList, loadMoreItems: loadMoreDepartments } = useDepartmentsList(
useMemo(() => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments }), [
useMemo(() => ({ filter: debouncedDepartmentsFilter, onlyMyDepartments, haveAll }), [
debouncedDepartmentsFilter,
onlyMyDepartments,
haveAll,
]),
);

Expand All @@ -29,6 +30,10 @@ const AutoCompleteDepartment = (props) => {
} = useRecordList(departmentsList);

const sortedByName = departmentsItems.sort((a, b) => {
if (a.value.value === 'all') {
return -1;
}

if (a.name > b.name) {
return 1;
}
Expand All @@ -47,7 +52,6 @@ const AutoCompleteDepartment = (props) => {
filter={departmentsFilter}
setFilter={setDepartmentsFilter}
options={sortedByName}
maxWidth='100%'
placeholder={t('Select_an_option')}
endReached={
departmentsPhase === AsyncStatePhase.LOADING
Expand Down
4 changes: 2 additions & 2 deletions client/components/GenericTable/HeaderCell.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Table } from '@rocket.chat/fuselage';
import React, { FC, useCallback } from 'react';
import React, { ComponentProps, FC, useCallback } from 'react';

import SortIcon from './SortIcon';

Expand All @@ -8,7 +8,7 @@ type HeaderCellProps = {
direction?: 'asc' | 'desc';
sort?: string;
onClick?: (sort: string) => void;
};
} & ComponentProps<typeof Box>;

const HeaderCell: FC<HeaderCellProps> = ({
children,
Expand Down
70 changes: 70 additions & 0 deletions client/components/Omnichannel/hooks/useAgentsList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useCallback, useState } from 'react';

import { ILivechatAgentRecord } from '../../../../definition/ILivechatAgentRecord';
import { useEndpoint } from '../../../contexts/ServerContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useScrollableRecordList } from '../../../hooks/lists/useScrollableRecordList';
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
import { RecordList } from '../../../lib/lists/RecordList';

type AgentsListOptions = {
text: string;
};

export const useAgentsList = (
options: AgentsListOptions,
): {
itemsList: RecordList<ILivechatAgentRecord>;
initialItemCount: number;
reload: () => void;
loadMoreItems: (start: number, end: number) => void;
} => {
const t = useTranslation();
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatAgentRecord>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatAgentRecord>()), []);
const endpoint = 'livechat/users/agent';

const getAgents = useEndpoint('GET', endpoint);

useComponentDidUpdate(() => {
options && reload();
}, [options, reload]);

const fetchData = useCallback(
async (start, end) => {
const { users: agents, total } = await getAgents({
...(options.text && { text: options.text }),
offset: start,
count: end + start,
sort: JSON.stringify({ name: 1 }),
});

const items = agents.map((agent: any) => {
agent._updatedAt = new Date(agent._updatedAt);
agent.label = agent.username;
agent.value = agent._id;
return agent;
});
items.unshift({
label: t('All'),
value: 'all',
_updatedAt: new Date(),
});

return {
items,
itemCount: total + 1,
};
},
[getAgents, options.text, t],
);

const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25);

return {
reload,
itemsList,
loadMoreItems,
initialItemCount,
};
};
47 changes: 33 additions & 14 deletions client/components/Omnichannel/hooks/useDepartmentsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useState } from 'react';

import { ILivechatDepartmentRecord } from '../../../../definition/ILivechatDepartmentRecord';
import { useEndpoint } from '../../../contexts/ServerContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useScrollableRecordList } from '../../../hooks/lists/useScrollableRecordList';
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
import { RecordList } from '../../../lib/lists/RecordList';
Expand All @@ -10,6 +11,7 @@ type DepartmentsListOptions = {
filter: string;
departmentId?: string;
onlyMyDepartments?: boolean;
haveAll?: boolean;
};

export const useDepartmentsList = (
Expand All @@ -20,6 +22,7 @@ export const useDepartmentsList = (
reload: () => void;
loadMoreItems: (start: number, end: number) => void;
} => {
const t = useTranslation();
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatDepartmentRecord>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatDepartmentRecord>()), []);
const endpoint = 'livechat/department' as 'livechat/department';
Expand All @@ -40,24 +43,40 @@ export const useDepartmentsList = (
sort: JSON.stringify({ name: 1 }),
});

const items = departments
.filter((department) => {
if (options.departmentId && department._id === options.departmentId) {
return false;
}
return true;
})
.map((department: any) => {
department._updatedAt = new Date(department._updatedAt);
department.label = department.name;
department.value = { value: department._id, label: department.name };
return department;
});

options.haveAll &&
items.unshift({
label: t('All'),
value: { value: 'all', label: t('All') },
_updatedAt: new Date(),
});

return {
items: departments
.filter((department) => {
if (options.departmentId && department._id === options.departmentId) {
return false;
}
return true;
})
.map((department: any) => {
department._updatedAt = new Date(department._updatedAt);
department.label = department.name;
department.value = { value: department._id, label: department.name };
return department;
}),
items,
itemCount: options.departmentId ? total - 1 : total,
};
},
[getDepartments, options],
[
getDepartments,
options.departmentId,
options.filter,
options.haveAll,
options.onlyMyDepartments,
t,
],
);

const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25);
Expand Down
1 change: 1 addition & 0 deletions client/components/Omnichannel/modals/ForwardChatModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const ForwardChatModal = ({ onForward, onCancel, room, ...props }) => {
<Field.Label>{t('Forward_to_department')}</Field.Label>
<Field.Row>
<PaginatedSelectFiltered
withTitle
filter={departmentsFilter}
setFilter={setDepartmentsFilter}
options={departmentsItems}
Expand Down
7 changes: 5 additions & 2 deletions client/components/Page/PageContent.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Box } from '@rocket.chat/fuselage';
import React, { forwardRef } from 'react';
import React, { ComponentProps, forwardRef } from 'react';

const PageContent = forwardRef(function PageContent(props, ref) {
const PageContent = forwardRef<HTMLElement, ComponentProps<typeof Box>>(function PageContent(
props,
ref,
) {
return (
<Box
ref={ref}
Expand Down
6 changes: 6 additions & 0 deletions client/contexts/ServerContext/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import { ListEndpoint as EmojiCustomListEndpoint } from './endpoints/v1/emoji-cu
import { FilesEndpoint as GroupsFilesEndpoint } from './endpoints/v1/groups/files';
import { FilesEndpoint as ImFilesEndpoint } from './endpoints/v1/im/files';
import { AppearanceEndpoint as LivechatAppearanceEndpoint } from './endpoints/v1/livechat/appearance';
import { LivechatCustomFieldsEndpoint } from './endpoints/v1/livechat/customFields';
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 { LivechatRoomsEndpoint } from './endpoints/v1/livechat/rooms';
import { LivechatTagsList } from './endpoints/v1/livechat/tagsList';
import { LivechatUsersAgentEndpoint } from './endpoints/v1/livechat/usersAgent';
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 @@ -48,6 +51,9 @@ export type ServerEndpoints = {
'livechat/tags.list': LivechatTagsList;
'livechat/department': LivechatDepartment;
'livechat/departments.by-unit/': LivechatDepartmentsByUnit;
'livechat/custom-fields': LivechatCustomFieldsEndpoint;
'livechat/rooms': LivechatRoomsEndpoint;
'livechat/users/agent': LivechatUsersAgentEndpoint;
};

export type ServerEndpointPath = keyof ServerEndpoints;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type LivechatCustomFieldsEndpoint = {
GET: (params: {}) => {
customFields: [
{
_id: string;
label: string;
},
];
};
};
15 changes: 15 additions & 0 deletions client/contexts/ServerContext/endpoints/v1/livechat/rooms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type LivechatRoomsEndpoint = {
GET: (params: {
guest: string;
fname: string;
servedBy: string[];
status: string;
department: string;
from: string;
to: string;
customFields: any;
current: number;
itemsPerPage: number;
tags: string[];
}) => { value: any; reload: () => void };
};
Loading