Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c288ea3
create and edit canned response endpoint
May 24, 2021
ca6bf85
fetch and create/edit polished
May 25, 2021
91cc5d3
delete endpoint to remove canned responses.
May 25, 2021
287e0aa
Merge branch 'develop' into omnichannel/canned-responses-api
rafaelblink May 26, 2021
88f5e25
[WIP] Canned Responses Page
MartinSchoeler May 26, 2021
6c96879
change default sort
May 26, 2021
1b26a13
Merge remote-tracking branch 'origin/omnichannel/canned-responses-api…
May 26, 2021
7264595
Merge remote-tracking branch 'origin/omnichannel/canned-responses-api…
MartinSchoeler May 26, 2021
ada3b32
More work in progress
MartinSchoeler Jun 1, 2021
b268014
Merge remote-tracking branch 'origin/improve/canned-response' into ca…
MartinSchoeler Jun 1, 2021
0023914
Merge remote-tracking branch 'origin/improve/canned-response' into ca…
MartinSchoeler Jun 1, 2021
27717de
More work in progress
MartinSchoeler Jun 1, 2021
34ec374
rename canned response alias
rafaelblink Jun 24, 2021
3ca5e63
Merge remote-tracking branch 'origin/develop' into cannedResponses-page
MartinSchoeler Jul 6, 2021
e295ddf
Update modal
MartinSchoeler Jul 6, 2021
0fd63d7
Merge branch 'improve/canned-response' into cannedResponses-page
MartinSchoeler Jul 6, 2021
6ae937c
Create autoCompleteTags and use autoCompleteAgents
MartinSchoeler Jul 7, 2021
a4c4d93
TS and other minor Fixes
MartinSchoeler Jul 8, 2021
b22fefd
Fix TS errors
MartinSchoeler Jul 9, 2021
c730350
Merge branch 'improve/canned-response' into cannedResponses-page
MartinSchoeler Jul 12, 2021
754ac8c
Remove Unrelated Files
MartinSchoeler Jul 12, 2021
e545149
move definition
MartinSchoeler Jul 12, 2021
0c5ca11
fix lint
MartinSchoeler Jul 12, 2021
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
2 changes: 2 additions & 0 deletions client/contexts/ServerContext/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { GroupsMembersEndpoint } from './endpoints/v1/groups/members';
import { FilesEndpoint as ImFilesEndpoint } from './endpoints/v1/im/files';
import { ImMembersEndpoint } from './endpoints/v1/im/members';
import { AppearanceEndpoint as LivechatAppearanceEndpoint } from './endpoints/v1/livechat/appearance';
import { CannedResponses } from './endpoints/v1/livechat/canned-responses';
import { LivechatCustomFieldsEndpoint } from './endpoints/v1/livechat/customFields';
import { LivechatDepartment } from './endpoints/v1/livechat/department';
import { LivechatDepartmentsByUnit } from './endpoints/v1/livechat/departmentsByUnit';
Expand Down Expand Up @@ -61,6 +62,7 @@ export type ServerEndpoints = {
'livechat/tags.list': LivechatTagsList;
'livechat/department': LivechatDepartment;
'livechat/departments.by-unit/': LivechatDepartmentsByUnit;
'canned-responses': CannedResponses;
'rooms.info': RoomsInfoEndpoint;
'users.2fa.sendEmailCode': SendEmailCodeEndpoint;
'livechat/custom-fields': LivechatCustomFieldsEndpoint;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ObjectFromApi } from '../../../../../../definition/ObjectFromApi';
import { IOmnichannelCannedResponse } from '../../../../../../ee/client/omnichannel/cannedResponses/IOmnichannelCannedResponse';

export type CannedResponses = {
GET: (params: { text: string; offset?: number | undefined; count?: number | undefined }) => {
cannedResponses: ObjectFromApi<IOmnichannelCannedResponse>[];
total: number;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ILivechatAgent } from '../../../../../../../definition/ILivechatAgent';
import { ObjectFromApi } from '../../../../../../../definition/ObjectFromApi';

export type LivechatAgents = {
GET: (params: { text: string; offset?: number | undefined; count?: number | undefined }) => {
tags: ObjectFromApi<ILivechatAgent>[];
total: number;
};
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type LivechatUsersAgentEndpoint = {
GET: (params?: { text?: string; offset: number; count: number; sort: string }) => {
GET: (params?: { text?: string; offset?: number; count?: number; sort?: string }) => {
users: {
_id: string;
emails: {
Expand Down
2 changes: 1 addition & 1 deletion client/views/omnichannel/departments/EditDepartment.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ function EditDepartment({ data, id, title, reload, allowedToForwardData }) {
<Page.Header title={title}>
<ButtonGroup>
{id && hasCannedResponsesLicense && cannedResponsesEnabled && (
<Button onClick={handleOpenCannedResponses} title={t('Canned Responses')}>
<Button onClick={handleOpenCannedResponses} title={t('Canned_Responses')}>
<Icon name='baloon-exclamation' size='x16' />
</Button>
)}
Expand Down
8 changes: 8 additions & 0 deletions definition/ILivechatAgent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface ILivechatAgent {
_id: string;
emails: { adress: string; verified: boolean };
status: string;
name: string;
username: string;
statusLivechat: string;
}
2 changes: 1 addition & 1 deletion ee/app/canned-responses/client/tabBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ addAction('canned-responses', () => {
return useMemo(() => (hasLicense && enabled ? {
groups: ['live'],
id: 'canned-responses',
title: 'Canned Responses',
title: 'Canned_Responses',
icon: 'canned-response',
template: 'cannedResponses',
order: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { addMessagePopup } from '../../../../../../app/ui-message/client/popup/c
import { t } from '../../../../../../app/utils';

addMessagePopup((template) => ({
title: t('Canned Responses'),
title: t('Canned_Responses'),
collection: CannedResponse,
trigger: '!',
prefix: '',
Expand Down
4 changes: 2 additions & 2 deletions ee/app/canned-responses/server/settings.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { settings } from '../../../../app/settings';

export const createSettings = () => {
settings.addGroup('Canned Responses', function() {
this.section('Canned Responses', function() {
settings.addGroup('Canned_Responses', function() {
this.section('Canned_Responses', function() {
this.add('Canned_Responses_Enable', false, {
type: 'boolean',
public: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ registerOmnichannelSidebarItem({
permissionGranted: () => hasPermission('manage-livechat-units'),
});

registerOmnichannelSidebarItem({
href: 'omnichannel-canned-responses',
i18nLabel: 'Canned_Responses',
permissionGranted: () => hasPermission('manage-livechat-canned-responses'),
});

registerOmnichannelSidebarItem({
href: 'omnichannel/tags',
i18nLabel: 'Tags',
Expand Down
1 change: 1 addition & 0 deletions ee/app/livechat-enterprise/server/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export const createPermissions = () => {
Permissions.create('manage-livechat-monitors', [adminRole, livechatManagerRole]);
Permissions.create('manage-livechat-tags', [adminRole, livechatManagerRole]);
Permissions.create('manage-livechat-priorities', [adminRole, livechatManagerRole]);
Permissions.create('manage-livechat-canned-responses', [adminRole, livechatManagerRole]);
};
64 changes: 64 additions & 0 deletions ee/client/hooks/useAgentsList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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 { ILivechatAgentRecord } from '../../../definition/ILivechatAgentRecord';

type AgentsListOptions = {
filter: string;
};

type getAgentsQuery = {
text: string;
offset: number;
count: number;
};

export const useAgentsList = (
options: AgentsListOptions,
): {
itemsList: RecordList<ILivechatAgentRecord>;
initialItemCount: number;
reload: () => void;
loadMoreItems: (start: number, end: number) => void;
} => {
const [itemsList, setItemsList] = useState(() => new RecordList<ILivechatAgentRecord>());
const reload = useCallback(() => setItemsList(new RecordList<ILivechatAgentRecord>()), []);

const getAgents = useEndpoint('GET', 'livechat/users/agent');

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

const fetchData = useCallback(
async (start, end) => {
const { users, total } = await getAgents({
text: options.filter,
offset: start,
count: end + start,
} as getAgentsQuery);
return {
items: users.map((agent: any) => {
agent._updatedAt = new Date(agent._updatedAt);
agent.label = agent.name;
agent.value = { value: agent._id, label: agent.name };
return agent;
}),
itemCount: total,
};
},
[getAgents, options.filter],
);

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

return {
reload,
itemsList,
loadMoreItems,
initialItemCount,
};
};
76 changes: 76 additions & 0 deletions ee/client/omnichannel/cannedResponses/CannedResponseFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Box, Icon, TextInput, Select, SelectOptions } from '@rocket.chat/fuselage';
import React, { FC, FormEvent, memo, useCallback } from 'react';

import AutoCompleteAgent from '../../../../client/components/AutoCompleteAgent';
import { useTranslation } from '../../../../client/contexts/TranslationContext';
import AutoCompleteTagsMultiple from '../tags/AutoCompleteTagsMultiple';

type CannedResponsesFilterProps = {
sharingValue: string;
createdByValue: { value: string; label: string };
tagsValue: string;
shortcutValue: string;
setSharing: (eventOrValue: unknown) => void;
setCreatedBy: (eventOrValue: unknown) => void;
setTags: (eventOrValue: unknown) => void;
setShortcut: (eventOrValue: unknown) => void;
};

const CannedResponsesFilter: FC<CannedResponsesFilterProps> = ({
sharingValue = '',
createdByValue = '',
tagsValue = '',
shortcutValue = '',
setSharing,
setCreatedBy,
setTags,
setShortcut,
...props
}) => {
const t = useTranslation();
const sharingList: SelectOptions = [
['', t('All')],
['private', t('Private')],
['public', t('Public')],
['department', t('Department')],
];

const handleFormSubmit = useCallback((event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
}, []);

return (
<Box
mb='x16'
is='form'
onSubmit={handleFormSubmit}
display='flex'
flexDirection='row'
{...props}
>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Box mb='x4'>{t('Search')}</Box>
<TextInput
addon={<Icon name='magnifier' size='x20' />}
onChange={setShortcut}
value={shortcutValue}
/>
</Box>

<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Box mb='x4'>{t('Sharing')}</Box>
<Select onChange={setSharing} options={sharingList} value={sharingValue} />
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Box mb='x4'>{t('Created_by')}</Box>
<AutoCompleteAgent onChange={setCreatedBy} value={createdByValue} haveAll />
</Box>
<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
<Box mb='x4'>{t('Tags')}</Box>
<AutoCompleteTagsMultiple onChange={setTags} value={tagsValue} />
</Box>
</Box>
);
};

export default memo(CannedResponsesFilter);
67 changes: 67 additions & 0 deletions ee/client/omnichannel/cannedResponses/CannedResponsesPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Button, Icon, ButtonGroup } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import React, { FC, ReactNode, ReactElement, Dispatch, SetStateAction } from 'react';

import GenericTable from '../../../../client/components/GenericTable';
import Page from '../../../../client/components/Page';
import { useRoute } from '../../../../client/contexts/RouterContext';
import { useTranslation } from '../../../../client/contexts/TranslationContext';
import { EndpointResponse } from './IOmnichannelCannedResponse';

export type CannedResponsesPageProps = {
data: EndpointResponse | undefined;
header: ReactElement[];
setParams: Dispatch<SetStateAction<{ current: number; itemsPerPage: number }>>;
params: { current: number; itemsPerPage: number };
title: string;
renderRow: ReactNode | null;
children: ReactNode | null;
renderFilter: FC;
};

const CannedResponsesPage: FC<CannedResponsesPageProps> = ({
data,
header,
setParams,
params,
title,
renderRow,
children,
renderFilter,
}) => {
const t = useTranslation();

const Route = useRoute('');

const handleClick = useMutableCallback(() =>
Route.push({
context: 'new',
}),
);

return (
<Page>
<Page.Header title={title}>
<ButtonGroup>
<Button onClick={handleClick} title={t('New_Canned_Response')}>
<Icon name='plus' /> {t('New')}
</Button>
</ButtonGroup>
</Page.Header>
<Page.Content>
<GenericTable
renderFilter={renderFilter}
header={header}
renderRow={renderRow}
results={data && data.cannedResponses}
total={data && data.total}
setParams={setParams}
params={params}
/>
</Page.Content>
{children}
</Page>
);
};

export default CannedResponsesPage;
Loading