diff --git a/app/apps/server/bridges/listeners.js b/app/apps/server/bridges/listeners.js
index 617e86801a799f43dc6a7de05f39e576bba78a60..d0cf92eec0bdc02a82039e403a59c62bb503dc1b 100644
--- a/app/apps/server/bridges/listeners.js
+++ b/app/apps/server/bridges/listeners.js
@@ -29,9 +29,6 @@ export class AppListenerBridge {
 				case AppInterface.IPreRoomUserJoined:
 				case AppInterface.IPostRoomUserJoined:
 					return 'roomEvent';
-				case AppInterface.IPostExternalComponentOpened:
-				case AppInterface.IPostExternalComponentClosed:
-					return 'externalComponentEvent';
 				/**
 				 * @deprecated please prefer the AppInterface.IPostLivechatRoomClosed event
 				 */
@@ -43,13 +40,20 @@ export class AppListenerBridge {
 				case AppInterface.IPostLivechatRoomTransferred:
 					return 'livechatEvent';
 				case AppInterface.IUIKitInteractionHandler:
-					return 'uiKitInteractionEvent';
+				case AppInterface.IUIKitLivechatInteractionHandler:
+				case AppInterface.IPostExternalComponentOpened:
+				case AppInterface.IPostExternalComponentClosed:
+					return 'defaultEvent';
 			}
 		})();
 
 		return this[method](event, ...payload);
 	}
 
+	async defaultEvent(inte, payload) {
+		return this.orch.getManager().getListenerManager().executeListener(inte, payload);
+	}
+
 	async messageEvent(inte, message) {
 		const msg = this.orch.getConverters().get('messages').convertMessage(message);
 		const result = await this.orch.getManager().getListenerManager().executeListener(inte, msg);
@@ -86,14 +90,6 @@ export class AppListenerBridge {
 		return this.orch.getConverters().get('rooms').convertAppRoom(result);
 	}
 
-	async externalComponentEvent(inte, externalComponent) {
-		return this.orch.getManager().getListenerManager().executeListener(inte, externalComponent);
-	}
-
-	async uiKitInteractionEvent(inte, action) {
-		return this.orch.getManager().getListenerManager().executeListener(inte, action);
-	}
-
 	async livechatEvent(inte, data) {
 		switch (inte) {
 			case AppInterface.IPostLivechatAgentAssigned:
diff --git a/app/apps/server/communication/uikit.js b/app/apps/server/communication/uikit.js
index ded3cb2b45e8a8cb98829db33fb2bd54d96dd9df..0eb8bb87d42daeab3221078d9411b0f1464526a1 100644
--- a/app/apps/server/communication/uikit.js
+++ b/app/apps/server/communication/uikit.js
@@ -3,9 +3,11 @@ import rateLimit from 'express-rate-limit';
 import { Meteor } from 'meteor/meteor';
 import { WebApp } from 'meteor/webapp';
 import { UIKitIncomingInteractionType } from '@rocket.chat/apps-engine/definition/uikit';
+import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata';
 
 import { Users } from '../../../models/server';
 import { settings } from '../../../settings/server';
+import { Apps } from '../orchestrator';
 
 const apiServer = express();
 
@@ -37,19 +39,21 @@ router.use((req, res, next) => {
 	const {
 		'x-user-id': userId,
 		'x-auth-token': authToken,
+		'x-visitor-token': visitorToken,
 	} = req.headers;
 
-	if (!userId || !authToken) {
-		return unauthorized(res);
+	if (userId && authToken) {
+		req.user = Users.findOneByIdAndLoginToken(userId, authToken);
+		req.userId = req.user._id;
 	}
 
-	const user = Users.findOneByIdAndLoginToken(userId, authToken);
-	if (!user) {
-		return unauthorized(res);
+	if (visitorToken) {
+		req.visitor = Apps.getConverters().get('visitors').convertByToken(visitorToken);
 	}
 
-	req.user = user;
-	req.userId = user._id;
+	if (!req.user && !req.visitor) {
+		return unauthorized(res);
+	}
 
 	next();
 });
@@ -81,6 +85,7 @@ export class AppUIKitInteractionApi {
 						container,
 					} = req.body;
 
+					const { visitor } = req;
 					const room = this.orch.getConverters().get('rooms').convertById(rid);
 					const user = this.orch.getConverters().get('users').convertToApp(req.user);
 					const message = mid && this.orch.getConverters().get('messages').convertById(mid);
@@ -94,11 +99,14 @@ export class AppUIKitInteractionApi {
 						triggerId,
 						payload,
 						user,
+						visitor,
 						room,
 					};
 
 					try {
-						const result = Promise.await(this.orch.getBridges().getListenerBridge().uiKitInteractionEvent('IUIKitInteractionHandler', action));
+						const eventInterface = !visitor ? AppInterface.IUIKitInteractionHandler : AppInterface.IUIKitLivechatInteractionHandler;
+
+						const result = Promise.await(this.orch.triggerEvent(eventInterface, action));
 
 						res.send(result);
 					} catch (e) {
@@ -131,7 +139,7 @@ export class AppUIKitInteractionApi {
 					};
 
 					try {
-						Promise.await(this.orch.getBridges().getListenerBridge().uiKitInteractionEvent('IUIKitInteractionHandler', action));
+						Promise.await(this.orch.triggerEvent('IUIKitInteractionHandler', action));
 
 						res.send(200);
 					} catch (e) {
@@ -161,7 +169,7 @@ export class AppUIKitInteractionApi {
 					};
 
 					try {
-						const result = Promise.await(this.orch.getBridges().getListenerBridge().uiKitInteractionEvent('IUIKitInteractionHandler', action));
+						const result = Promise.await(this.orch.triggerEvent('IUIKitInteractionHandler', action));
 
 						res.send(result);
 					} catch (e) {
diff --git a/app/ui-flextab/client/tabs/userActions.js b/app/ui-flextab/client/tabs/userActions.js
index deda9cbc7715bdf326d6c5612f702f37fd84a08c..5af08975d72e8893be00ab40ca219c56ec2eb1b5 100644
--- a/app/ui-flextab/client/tabs/userActions.js
+++ b/app/ui-flextab/client/tabs/userActions.js
@@ -4,6 +4,7 @@ import { Session } from 'meteor/session';
 import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
 import toastr from 'toastr';
 import _ from 'underscore';
+import s from 'underscore.string';
 
 import { WebRTC } from '../../../webrtc/client';
 import { ChatRoom, ChatSubscription, RoomRoles, Subscriptions } from '../../../models/client';
@@ -223,7 +224,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 						}
 						Meteor.call('removeRoomOwner', Session.get('openedRoom'), _id, success(() => {
 							const room = ChatRoom.findOne(Session.get('openedRoom'));
-							toastr.success(TAPi18n.__('User__username__removed_from__room_name__owners', { username, room_name: roomTypes.getRoomName(room.t, room) }));
+							toastr.success(TAPi18n.__('User__username__removed_from__room_name__owners', { username, room_name: s.escapeHTML(roomTypes.getRoomName(room.t, room)) }));
 						}));
 					}) };
 			}
@@ -238,7 +239,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 					}
 					Meteor.call('addRoomOwner', Session.get('openedRoom'), _id, success(() => {
 						const room = ChatRoom.findOne(Session.get('openedRoom'));
-						toastr.success(TAPi18n.__('User__username__is_now_a_owner_of__room_name_', { username, room_name: roomTypes.getRoomName(room.t, room) }));
+						toastr.success(TAPi18n.__('User__username__is_now_a_owner_of__room_name_', { username, room_name: s.escapeHTML(roomTypes.getRoomName(room.t, room)) }));
 					}));
 				}),
 			};
@@ -261,7 +262,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 						}
 						Meteor.call('removeRoomLeader', Session.get('openedRoom'), _id, success(() => {
 							const room = ChatRoom.findOne(Session.get('openedRoom'));
-							toastr.success(TAPi18n.__('User__username__removed_from__room_name__leaders', { username, room_name: roomTypes.getRoomName(room.t, room) }));
+							toastr.success(TAPi18n.__('User__username__removed_from__room_name__leaders', { username, room_name: s.escapeHTML(roomTypes.getRoomName(room.t, room)) }));
 						}));
 					}),
 				};
@@ -277,7 +278,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 					}
 					Meteor.call('addRoomLeader', Session.get('openedRoom'), _id, success(() => {
 						const room = ChatRoom.findOne(Session.get('openedRoom'));
-						toastr.success(TAPi18n.__('User__username__is_now_a_leader_of__room_name_', { username, room_name: roomTypes.getRoomName(room.t, room) }));
+						toastr.success(TAPi18n.__('User__username__is_now_a_leader_of__room_name_', { username, room_name: s.escapeHTML(roomTypes.getRoomName(room.t, room)) }));
 					}));
 				}),
 			};
@@ -300,7 +301,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 						}
 						Meteor.call('removeRoomModerator', Session.get('openedRoom'), _id, success(() => {
 							const room = ChatRoom.findOne(Session.get('openedRoom'));
-							toastr.success(TAPi18n.__('User__username__removed_from__room_name__moderators', { username, room_name: roomTypes.getRoomName(room.t, room) }));
+							toastr.success(TAPi18n.__('User__username__removed_from__room_name__moderators', { username, room_name: s.escapeHTML(roomTypes.getRoomName(room.t, room)) }));
 						}));
 					}),
 				};
@@ -316,7 +317,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 					}
 					Meteor.call('addRoomModerator', Session.get('openedRoom'), _id, success(() => {
 						const room = ChatRoom.findOne(Session.get('openedRoom'));
-						toastr.success(TAPi18n.__('User__username__is_now_a_moderator_of__room_name_', { username, room_name: roomTypes.getRoomName(room.t, room) }));
+						toastr.success(TAPi18n.__('User__username__is_now_a_moderator_of__room_name_', { username, room_name: s.escapeHTML(roomTypes.getRoomName(room.t, room)) }));
 					}));
 				}),
 			};
@@ -374,7 +375,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 					}
 					modal.open({
 						title: t('Are_you_sure'),
-						text: t('The_user_wont_be_able_to_type_in_s', roomTypes.getRoomName(room.t, room)),
+						text: t('The_user_wont_be_able_to_type_in_s', s.escapeHTML(roomTypes.getRoomName(room.t, room))),
 						type: 'warning',
 						showCancelButton: true,
 						confirmButtonColor: '#DD6B55',
@@ -386,7 +387,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 						Meteor.call('muteUserInRoom', { rid, username }, success(() => {
 							modal.open({
 								title: t('Muted'),
-								text: t('User_has_been_muted_in_s', roomTypes.getRoomName(room.t, room)),
+								text: t('User_has_been_muted_in_s', s.escapeHTML(roomTypes.getRoomName(room.t, room))),
 								type: 'success',
 								timer: 2000,
 								showConfirmButton: false,
@@ -408,7 +409,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 				}
 				modal.open({
 					title: t('Are_you_sure'),
-					text: t('The_user_will_be_removed_from_s', roomTypes.getRoomName(room.t, room)),
+					text: t('The_user_will_be_removed_from_s', s.escapeHTML(roomTypes.getRoomName(room.t, room))),
 					type: 'warning',
 					showCancelButton: true,
 					confirmButtonColor: '#DD6B55',
@@ -419,7 +420,7 @@ export const getActions = ({ user, directActions, hideAdminControls }) => {
 				}, () => Meteor.call('removeUserFromRoom', { rid, username: user.username }, success(() => {
 					modal.open({
 						title: t('Removed'),
-						text: t('User_has_been_removed_from_s', roomTypes.getRoomName(room.t, room)),
+						text: t('User_has_been_removed_from_s', s.escapeHTML(roomTypes.getRoomName(room.t, room))),
 						type: 'success',
 						timer: 2000,
 						showConfirmButton: false,
diff --git a/client/channel/ExportMessages/index.js b/client/channel/ExportMessages/index.js
index 7dbc537b3fa32e93d64b7677b2ffa1308e04f25e..93b0e06dfefd91631c91eb6b5d20df817b792187 100644
--- a/client/channel/ExportMessages/index.js
+++ b/client/channel/ExportMessages/index.js
@@ -5,6 +5,7 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import toastr from 'toastr';
 
 import VerticalBar from '../../components/basic/VerticalBar';
+import { UserAutoComplete } from '../../components/basic/AutoComplete';
 import { useTranslation } from '../../contexts/TranslationContext';
 import { useForm } from '../../hooks/useForm';
 import { useUserRoom } from '../hooks/useUserRoom';
@@ -63,9 +64,14 @@ const FileExport = ({ onCancel, rid }) => {
 	return (
 		<FieldGroup>
 			<Field>
-				<Field.Label>{t('Date')}</Field.Label>
+				<Field.Label>{t('Date_From')}</Field.Label>
 				<Field.Row>
 					<TextInput type='date' value={dateFrom} onChange={handleDateFrom} />
+				</Field.Row>
+			</Field>
+			<Field>
+				<Field.Label>{t('Date_to')}</Field.Label>
+				<Field.Row>
 					<TextInput type='date' value={dateTo} onChange={handleDateTo} />
 				</Field.Row>
 			</Field>
@@ -196,7 +202,7 @@ const MailExportForm = ({ onCancel, rid }) => {
 			<Field>
 				<Field.Label>{t('To_users')}</Field.Label>
 				<Field.Row>
-					<TextInput placeholder={t('Username_Placeholder')} value={toUsers} onChange={handleToUsers} addon={<Icon name='at' size='x20'/>} />
+					<UserAutoComplete value={toUsers} onChange={handleToUsers}/>
 				</Field.Row>
 			</Field>
 			<Field>
diff --git a/client/channel/hooks/useUserInfoActions.js b/client/channel/hooks/useUserInfoActions.js
index 720f041f02a8b99e11aa5a737a2a9f17f29d7237..155511438e81ccbc733c18dc057ec421992d1a89 100644
--- a/client/channel/hooks/useUserInfoActions.js
+++ b/client/channel/hooks/useUserInfoActions.js
@@ -1,6 +1,7 @@
 import React, { useCallback, useMemo } from 'react';
 import { Button, ButtonGroup, Icon, Modal, Box } from '@rocket.chat/fuselage';
 import { useAutoFocus, useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import s from 'underscore.string';
 
 import { useTranslation } from '../../contexts/TranslationContext';
 import { useReactiveValue } from '../../hooks/useReactiveValue';
@@ -145,7 +146,7 @@ export const useUserInfoActions = (user = {}, rid) => {
 		},
 	};
 
-	const roomName = room && room.t && roomTypes.getRoomName(room.t, room);
+	const roomName = room && room.t && s.escapeHTML(roomTypes.getRoomName(room.t, room));
 
 	const userCanSetOwner = usePermission('set-owner', rid);
 	const userCanSetLeader = usePermission('set-leader', rid);
diff --git a/client/components/basic/AutoCompleteAgent.js b/client/components/basic/AutoCompleteAgent.js
new file mode 100644
index 0000000000000000000000000000000000000000..5e42fdd0df5fb908767fa98dc8539eb3a98aaaf0
--- /dev/null
+++ b/client/components/basic/AutoCompleteAgent.js
@@ -0,0 +1,22 @@
+import React, { useMemo, useState } from 'react';
+import { AutoComplete, Option } from '@rocket.chat/fuselage';
+
+import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
+import { useTranslation } from '../../contexts/TranslationContext';
+
+export const AutoCompleteAgent = React.memo((props) => {
+	const t = useTranslation();
+	const [filter, setFilter] = useState('');
+	const { data } = useEndpointDataExperimental('livechat/users/agent', useMemo(() => ({ text: filter }), [filter]));
+
+	const options = useMemo(() => (data && [{ value: 'all', label: t('All') }, ...data.users.map((user) => ({ value: user._id, label: user.name }))]) || [{ value: 'all', label: t('All') }], [data, t]);
+
+	return <AutoComplete
+		{...props}
+		filter={filter}
+		setFilter={setFilter}
+		renderSelected={({ label }) => <>{label}</>}
+		renderItem={({ value, ...props }) => <Option key={value} {...props} />}
+		options={ options }
+	/>;
+});
diff --git a/client/components/basic/AutoCompleteDepartment.js b/client/components/basic/AutoCompleteDepartment.js
new file mode 100644
index 0000000000000000000000000000000000000000..0fa8784cf91158cc4c6b75ffad2f9bc63b0d3d02
--- /dev/null
+++ b/client/components/basic/AutoCompleteDepartment.js
@@ -0,0 +1,22 @@
+import React, { useMemo, useState } from 'react';
+import { AutoComplete, Option } from '@rocket.chat/fuselage';
+
+import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
+import { useTranslation } from '../../contexts/TranslationContext';
+
+export const AutoCompleteDepartment = React.memo((props) => {
+	const t = useTranslation();
+	const [filter, setFilter] = useState('');
+	const { data } = useEndpointDataExperimental('livechat/department', useMemo(() => ({ text: filter }), [filter]));
+
+	const options = useMemo(() => (data && [{ value: 'all', label: t('All') }, ...data.departments.map((department) => ({ value: department._id, label: department.name }))]) || [{ value: 'all', label: t('All') }], [data, t]);
+
+	return <AutoComplete
+		{...props}
+		filter={filter}
+		setFilter={setFilter}
+		renderSelected={({ label }) => <>{label}</>}
+		renderItem={({ value, ...props }) => <Option key={value} {...props} />}
+		options={ options }
+	/>;
+});
diff --git a/client/omnichannel/agents/AgentEdit.js b/client/omnichannel/agents/AgentEdit.js
index f7fa60ae8bf7dda9f14d672d52066f744358729b..4654e77a5c21f87b8dadba8ece5b8acc48111e0c 100644
--- a/client/omnichannel/agents/AgentEdit.js
+++ b/client/omnichannel/agents/AgentEdit.js
@@ -26,7 +26,7 @@ export default function AgentEditWithData({ uid, reload }) {
 		return <FormSkeleton/>;
 	}
 
-	if (error || userDepartmentsError || availableDepartmentsError) {
+	if (error || userDepartmentsError || availableDepartmentsError || !data || !data.user) {
 		return <Box mbs='x16'>{t('User_not_found')}</Box>;
 	}
 
diff --git a/client/omnichannel/agents/AgentInfo.js b/client/omnichannel/agents/AgentInfo.js
index ad5159ce56a922cc4057ceacd4ae5c69d5d65d05..153ddaf09717879fcfd78b209197e5dba003f673 100644
--- a/client/omnichannel/agents/AgentInfo.js
+++ b/client/omnichannel/agents/AgentInfo.js
@@ -30,11 +30,11 @@ export const AgentInfo = React.memo(function AgentInfo({
 		return <FormSkeleton/>;
 	}
 
-	if (error) {
+	if (error || !data || !data.user) {
 		return <Box mbs='x16'>{t('User_not_found')}</Box>;
 	}
 
-	const { user } = data || { user: {} };
+	const { user } = data;
 	const {
 		username,
 		statusLivechat,
diff --git a/client/omnichannel/agents/AgentsRoute.js b/client/omnichannel/agents/AgentsRoute.js
index 2e511de4a413fb2acc4e410f1ca9f08a900205f3..acac7a3cd657c30dbaf77f8f84e96949b5c29d3f 100644
--- a/client/omnichannel/agents/AgentsRoute.js
+++ b/client/omnichannel/agents/AgentsRoute.js
@@ -1,7 +1,7 @@
 
 import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useMemo, useCallback, useState } from 'react';
-import { Box, Table, Icon } from '@rocket.chat/fuselage';
+import { Box, Table, Icon, Button } from '@rocket.chat/fuselage';
 
 import { Th } from '../../components/GenericTable';
 import { useTranslation } from '../../contexts/TranslationContext';
@@ -15,19 +15,45 @@ import AgentInfo from './AgentInfo';
 import UserAvatar from '../../components/basic/avatar/UserAvatar';
 import { useRouteParameter, useRoute } from '../../contexts/RouterContext';
 import VerticalBar from '../../components/basic/VerticalBar';
+import DeleteWarningModal from '../DeleteWarningModal';
+import { useSetModal } from '../../contexts/ModalContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+
 
 export function RemoveAgentButton({ _id, reload }) {
 	const deleteAction = useEndpointAction('DELETE', `livechat/users/agent/${ _id }`);
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
+
 
-	const handleRemoveClick = useMutableCallback(async (e) => {
-		e.preventDefault();
+	const handleRemoveClick = useMutableCallback(async () => {
 		const result = await deleteAction();
 		if (result.success === true) {
 			reload();
 		}
 	});
 
-	return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Agent_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
 }
 
 export function AgentInfoActions({ reload }) {
@@ -35,6 +61,8 @@ export function AgentInfoActions({ reload }) {
 	const _id = useRouteParameter('id');
 	const agentsRoute = useRoute('omnichannel-agents');
 	const deleteAction = useEndpointAction('DELETE', `livechat/users/agent/${ _id }`);
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
 
 	const handleRemoveClick = useMutableCallback(async () => {
 		const result = await deleteAction();
@@ -44,13 +72,28 @@ export function AgentInfoActions({ reload }) {
 		}
 	});
 
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Agent_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
 	const handleEditClick = useMutableCallback(() => agentsRoute.push({
 		context: 'edit',
 		id: _id,
 	}));
 
 	return [
-		<AgentInfo.Action key={t('Remove')} title={t('Remove')} label={t('Remove')} onClick={handleRemoveClick} icon={'trash'} />,
+		<AgentInfo.Action key={t('Remove')} title={t('Remove')} label={t('Remove')} onClick={handleDelete} icon={'trash'} />,
 		<AgentInfo.Action key={t('Edit')} title={t('Edit')} label={t('Edit')} onClick={handleEditClick} icon={'edit'} />,
 	];
 }
diff --git a/client/omnichannel/businessHours/EditBusinessHoursPage.js b/client/omnichannel/businessHours/EditBusinessHoursPage.js
index 305be5b77d7e124f0c6d70b09e985c5197544d6e..51db2695949e9fa9710f9bbe305a09faa306b888 100644
--- a/client/omnichannel/businessHours/EditBusinessHoursPage.js
+++ b/client/omnichannel/businessHours/EditBusinessHoursPage.js
@@ -57,6 +57,7 @@ const EditBusinessHoursPage = ({ id, type }) => {
 
 			await save(payload);
 			dispatchToastMessage({ type: 'success', message: t('Business_hours_updated') });
+			router.push({});
 		} catch (error) {
 			dispatchToastMessage({ type: 'error', message: error });
 		}
diff --git a/client/omnichannel/currentChats/CurrentChatsPage.js b/client/omnichannel/currentChats/CurrentChatsPage.js
index 3d2806880d5a0e71daa2d78d9bc921a1dbc0010a..01cae3222db18b408a489e087c57ff9ece400db4 100644
--- a/client/omnichannel/currentChats/CurrentChatsPage.js
+++ b/client/omnichannel/currentChats/CurrentChatsPage.js
@@ -1,6 +1,6 @@
 import React, { useEffect, useMemo } from 'react';
 import { TextInput, Box, Icon, MultiSelect, Select, InputBox, Menu } from '@rocket.chat/fuselage';
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { useMutableCallback, useLocalStorage } from '@rocket.chat/fuselage-hooks';
 import moment from 'moment';
 import { useSubscription } from 'use-subscription';
 
@@ -10,12 +10,13 @@ import { useTranslation } from '../../contexts/TranslationContext';
 import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
 import { usePermission } from '../../contexts/AuthorizationContext';
 import { GenericTable } from '../../components/GenericTable';
-import { useForm } from '../../hooks/useForm';
 import { useMethod } from '../../contexts/ServerContext';
+import DeleteWarningModal from '../DeleteWarningModal';
+import { useSetModal } from '../../contexts/ModalContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { AutoCompleteDepartment } from '../../components/basic/AutoCompleteDepartment';
+import { AutoCompleteAgent } from '../../components/basic/AutoCompleteAgent';
 
-
-// moment(new Date(from)).utc().format('YYYY-MM-DDTHH:mm:ss')
-// guest: '', servedBy: '', status: '', department: '', from: '', to: ''
 const Label = (props) => <Box fontScale='p2' color='default' {...props} />;
 
 const RemoveAllClosed = ({ handleClearFilters, handleRemoveClosed, ...props }) => {
@@ -43,38 +44,42 @@ const RemoveAllClosed = ({ handleClearFilters, handleRemoveClosed, ...props }) =
 
 
 const FilterByText = ({ setFilter, reload, ...props }) => {
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
 	const t = useTranslation();
 
-	const { data: departments } = useEndpointDataExperimental('livechat/department') || {};
-	const { data: agents } = useEndpointDataExperimental('livechat/users/agent');
-
-	const depOptions = useMemo(() => (departments && departments.departments ? departments.departments.map(({ _id, name }) => [_id, name || _id]) : []), [departments]);
-	const agentOptions = useMemo(() => (agents && agents.users ? agents.users.map(({ _id, username }) => [_id, username || _id]) : []), [agents]);
+	const { data: allCustomFields } = useEndpointDataExperimental('livechat/custom-fields');
 	const statusOptions = [['all', t('All')], ['closed', t('Closed')], ['opened', t('Open')]];
-
-	useEffect(() => {
-		!depOptions.find((dep) => dep[0] === 'all') && depOptions.unshift(['all', t('All')]);
-	}, [depOptions, t]);
-
-	const { values, handlers, reset } = useForm({ guest: '', servedBy: [], status: 'all', department: 'all', from: '', to: '', tags: [] });
-	const {
-		handleGuest,
-		handleServedBy,
-		handleStatus,
-		handleDepartment,
-		handleFrom,
-		handleTo,
-		handleTags,
-	} = handlers;
-	const {
-		guest,
-		servedBy,
-		status,
-		department,
-		from,
-		to,
-		tags,
-	} = values;
+	const customFieldsOptions = useMemo(() => (allCustomFields && allCustomFields.customFields ? allCustomFields.customFields.map(({ _id, label }) => [_id, label]) : []), [allCustomFields]);
+
+	const [guest, setGuest] = useLocalStorage('guest', '');
+	const [servedBy, setServedBy] = useLocalStorage('servedBy', 'all');
+	const [status, setStatus] = useLocalStorage('status', 'all');
+	const [department, setDepartment] = useLocalStorage('department', 'all');
+	const [from, setFrom] = useLocalStorage('from', '');
+	const [to, setTo] = useLocalStorage('to', '');
+	const [tags, setTags] = useLocalStorage('tags', []);
+	const [customFields, setCustomFields] = useLocalStorage('tags', []);
+
+	const handleGuest = useMutableCallback((e) => setGuest(e.target.value));
+	const handleServedBy = useMutableCallback((e) => setServedBy(e));
+	const handleStatus = useMutableCallback((e) => setStatus(e));
+	const handleDepartment = useMutableCallback((e) => setDepartment(e));
+	const handleFrom = useMutableCallback((e) => setFrom(e.target.value));
+	const handleTo = useMutableCallback((e) => setTo(e.target.value));
+	const handleTags = useMutableCallback((e) => setTags(e));
+	const handleCustomFields = useMutableCallback((e) => setCustomFields(e));
+
+	const reset = useMutableCallback(() => {
+		setGuest('');
+		setServedBy('all');
+		setStatus('all');
+		setDepartment('all');
+		setFrom('');
+		setTo('');
+		setTags([]);
+		setCustomFields([]);
+	});
 
 	const forms = useSubscription(formsSubscription);
 
@@ -84,8 +89,11 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
 
 	const Tags = useCurrentChatTags();
 
-
 	const onSubmit = useMutableCallback((e) => e.preventDefault());
+	const reducer = function(acc, curr) {
+		acc[curr] = '';
+		return acc;
+	};
 
 	useEffect(() => {
 		setFilter({
@@ -96,8 +104,9 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
 			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,
+			customFields: customFields.reduce(reducer, {}),
 		});
-	}, [setFilter, guest, servedBy, status, department, from, to, tags]);
+	}, [setFilter, guest, servedBy, status, department, from, to, tags, customFields]);
 
 	const handleClearFilters = useMutableCallback(() => {
 		reset();
@@ -106,10 +115,21 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
 	const removeClosedChats = useMethod('livechat:removeAllClosedRooms');
 
 	const handleRemoveClosed = useMutableCallback(async () => {
-		await removeClosedChats();
-		reload();
+		const onDeleteAll = async () => {
+			try {
+				await removeClosedChats();
+				reload();
+				dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAll} onCancel={() => setModal()}/>);
 	});
 
+
 	return <Box mb='x16' is='form' onSubmit={onSubmit} display='flex' flexDirection='column' {...props}>
 		<Box display='flex' flexDirection='row' flexWrap='wrap' {...props}>
 			<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
@@ -118,11 +138,11 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
 			</Box>
 			<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
 				<Label mb='x4'>{t('Served_By')}:</Label>
-				<MultiSelect flexShrink={0} options={agentOptions} value={servedBy} onChange={handleServedBy} placeholder={t('Served_By')}/>
+				<AutoCompleteAgent value={servedBy} onChange={handleServedBy}/>
 			</Box>
 			<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
 				<Label mb='x4'>{t('Department')}:</Label>
-				<Select flexShrink={0} options={depOptions} value={department} onChange={handleDepartment} placeholder={t('Department')}/>
+				<AutoCompleteDepartment value={department} onChange={handleDepartment}/>
 			</Box>
 			<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
 				<Label mb='x4'>{t('Status')}:</Label>
@@ -145,6 +165,12 @@ const FilterByText = ({ setFilter, reload, ...props }) => {
 				<Tags value={tags} handler={handleTags} />
 			</Box>
 		</Box>}
+		{allCustomFields && <Box display='flex' flexDirection='row' marginBlockStart='x8' {...props}>
+			<Box display='flex' mie='x8' flexGrow={1} flexDirection='column'>
+				<Label mb='x4'>{t('Custom_fields')}:</Label>
+				<MultiSelect options={customFieldsOptions} value={customFields} onChange={handleCustomFields} flexGrow={1} {...props}/>
+			</Box>
+		</Box>}
 	</Box>;
 };
 
@@ -156,7 +182,6 @@ function CurrentChatsPage({
 	params,
 	title,
 	renderRow,
-	departments,
 	reload,
 	children,
 }) {
@@ -164,7 +189,7 @@ function CurrentChatsPage({
 		<Page>
 			<Page.Header title={title} />
 			<Page.Content>
-				<GenericTable FilterComponent={FilterByText} header={header} renderRow={renderRow} results={data && data.rooms} departments={departments} total={data && data.total} setParams={setParams} params={params} reload={reload}/>
+				<GenericTable FilterComponent={FilterByText} header={header} renderRow={renderRow} results={data && data.rooms} total={data && data.total} setParams={setParams} params={params} reload={reload}/>
 			</Page.Content>
 		</Page>
 		{children}
diff --git a/client/omnichannel/currentChats/CurrentChatsRoute.js b/client/omnichannel/currentChats/CurrentChatsRoute.js
index 18fb5ced1f00407edc9a02098ea4502cfc66a0a7..da9edc10ea86010259cdfc1acd84dc27c0263aeb 100644
--- a/client/omnichannel/currentChats/CurrentChatsRoute.js
+++ b/client/omnichannel/currentChats/CurrentChatsRoute.js
@@ -2,7 +2,7 @@
 
 import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useMemo, useCallback, useState } from 'react';
-import { Table, Icon } from '@rocket.chat/fuselage';
+import { Table, Icon, Button } from '@rocket.chat/fuselage';
 import moment from 'moment';
 import { FlowRouter } from 'meteor/kadira:flow-router';
 
@@ -13,35 +13,54 @@ import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperime
 import { useMethod } from '../../contexts/ServerContext';
 import { usePermission } from '../../contexts/AuthorizationContext';
 import NotAuthorizedPage from '../../components/NotAuthorizedPage';
-import { useRoute } from '../../contexts/RouterContext';
 import CurrentChatsPage from './CurrentChatsPage';
+import DeleteWarningModal from '../DeleteWarningModal';
+import { useSetModal } from '../../contexts/ModalContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+
+export function RemoveChatButton({ _id, reload }) {
+	const removeChat = useMethod('livechat:removeRoom');
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
 
-export function RemoveCurrentChatButton({ _id, reload }) {
-	const removeCurrentChat = useMethod('livechat:removeCurrentChat');
-	const currentChatsRoute = useRoute('omnichannel-currentChats');
-
-
-	const handleRemoveClick = useMutableCallback(async (e) => {
-		e.preventDefault();
-		e.stopPropagation();
+	const handleRemoveClick = useMutableCallback(async () => {
 		try {
-			await removeCurrentChat(_id);
+			await removeChat(_id);
 		} catch (error) {
 			console.log(error);
 		}
-		currentChatsRoute.push({});
 		reload();
 	});
 
-	return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Chat_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint'withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
 }
 
 const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);
 
-const useQuery = ({ guest, servedBy, department, status, from, to, tags, itemsPerPage, current }, [column, direction]) => useMemo(() => {
+const useQuery = ({ guest, servedBy, department, status, from, to, tags, customFields, itemsPerPage, current }, [column, direction]) => useMemo(() => {
 	const query = {
-		roomName: guest,
-		sort: JSON.stringify({ [column]: sortDir(direction), usernames: column === 'name' ? sortDir(direction) : undefined }),
+		...guest && { roomName: guest },
+		sort: JSON.stringify({ [column]: sortDir(direction), ts: column === 'ts' ? sortDir(direction) : undefined }),
 		...itemsPerPage && { count: itemsPerPage },
 		...current && { offset: current },
 	};
@@ -52,27 +71,30 @@ const useQuery = ({ guest, servedBy, department, status, from, to, tags, itemsPe
 	if (status !== 'all') {
 		query.open = status === 'open';
 	}
-	if (servedBy && servedBy.length > 0) {
-		query.agents = servedBy;
+	if (servedBy && servedBy !== 'all') {
+		query.agents = [servedBy];
 	}
-	if (department && department.length > 0) {
-		if (department !== 'all') {
-			query.departmentId = department;
-		}
+	if (department && department !== 'all') {
+		query.departmentId = department;
 	}
+
 	if (tags && tags.length > 0) {
 		query.tags = tags;
 	}
 
+	if (customFields && Object.keys(customFields).length > 0) {
+		query.customFields = JSON.stringify(customFields);
+	}
+
 	return query;
-}, [guest, column, direction, itemsPerPage, current, from, to, status, servedBy, department, tags]);
+}, [guest, column, direction, itemsPerPage, current, from, to, status, servedBy, department, tags, customFields]);
 
 function CurrentChatsRoute() {
 	const t = useTranslation();
 	const canViewCurrentChats = usePermission('view-livechat-current-chats');
 
-	const [params, setParams] = useState({ fname: '', servedBy: [], status: '', department: '', from: '', to: '', current: 0, itemsPerPage: 25 });
-	const [sort, setSort] = useState(['name', 'asc']);
+	const [params, setParams] = useState({ fname: '', servedBy: [], status: '', department: '', from: '', to: '', customFields: {}, current: 0, itemsPerPage: 25 });
+	const [sort, setSort] = useState(['ts', 'desc']);
 
 	const debouncedParams = useDebouncedValue(params, 500);
 	const debouncedSort = useDebouncedValue(sort, 500);
@@ -96,15 +118,15 @@ function CurrentChatsRoute() {
 	});
 
 	const { data, reload } = useEndpointDataExperimental('livechat/rooms', query) || {};
-	const { data: departments } = useEndpointDataExperimental('livechat/department', query) || {};
 
 	const header = useMemo(() => [
 		<Th key={'name'} direction={sort[1]} active={sort[0] === 'name'} onClick={onHeaderClick} sort='name' w='x120'>{t('Name')}</Th>,
 		<Th key={'departmentId'} direction={sort[1]} active={sort[0] === 'departmentId'} onClick={onHeaderClick} sort='departmentId' w='x200'>{t('Department')}</Th>,
-		<Th key={'servedBy'} direction={sort[1]} active={sort[0] === 'servedBy'} onClick={onHeaderClick} sort='servedBy' w='x120'>{t('Served_by')}</Th>,
-		<Th key={'ts'} direction={sort[1]} active={sort[0] === 'ts'} onClick={onHeaderClick} sort='ts' w='x120'>{t('Started_at')}</Th>,
-		<Th key={'lm'} direction={sort[1]} active={sort[0] === 'lm'} onClick={onHeaderClick} sort='visibility' w='x120'>{t('Last_message')}</Th>,
+		<Th key={'servedBy'} direction={sort[1]} active={sort[0] === 'servedBy'} onClick={onHeaderClick} sort='servedBy' w='x120'>{t('Served_By')}</Th>,
+		<Th key={'ts'} direction={sort[1]} active={sort[0] === 'ts'} onClick={onHeaderClick} sort='ts' w='x120'>{t('Started_At')}</Th>,
+		<Th key={'lm'} direction={sort[1]} active={sort[0] === 'lm'} onClick={onHeaderClick} sort='visibility' w='x120'>{t('Last_Message')}</Th>,
 		<Th key={'status'} direction={sort[1]} active={sort[0] === 'status'} onClick={onHeaderClick} sort='status' w='x120'>{t('Status')}</Th>,
+		<Th key={'remove'} w='x40'>{t('Remove')}</Th>,
 	].filter(Boolean), [sort, onHeaderClick, t]);
 
 	const renderRow = useCallback(({ _id, fname, servedBy, ts, lm, department, open }) => <Table.Row key={_id} tabIndex={0} role='link' onClick={() => onRowClick(_id)} action qa-user-id={_id}>
@@ -114,7 +136,8 @@ function CurrentChatsRoute() {
 		<Table.Cell withTruncatedText>{moment(ts).format('L LTS')}</Table.Cell>
 		<Table.Cell withTruncatedText>{moment(lm).format('L LTS')}</Table.Cell>
 		<Table.Cell withTruncatedText>{open ? t('Open') : t('Closed')}</Table.Cell>
-	</Table.Row>, [onRowClick, t]);
+		{!open && <RemoveChatButton _id={_id} reload={reload}/>}
+	</Table.Row>, [onRowClick, reload, t]);
 
 	if (!canViewCurrentChats) {
 		return <NotAuthorizedPage />;
@@ -129,7 +152,6 @@ function CurrentChatsRoute() {
 		reload={reload}
 		header={header}
 		renderRow={renderRow}
-		departments={departments}
 		title={'Current Chats'}>
 	</CurrentChatsPage>;
 }
diff --git a/client/omnichannel/customFields/CustomFieldsTable.js b/client/omnichannel/customFields/CustomFieldsTable.js
index 4eefebd5231ee9955b10c15ee831d8e8bc7f9fdb..1ad532bfef7a2d70c3991ea02daa64b9f908525a 100644
--- a/client/omnichannel/customFields/CustomFieldsTable.js
+++ b/client/omnichannel/customFields/CustomFieldsTable.js
@@ -1,15 +1,59 @@
-import { Icon, Table, Button, Callout } from '@rocket.chat/fuselage';
+import { Icon, Table, Callout, Button } from '@rocket.chat/fuselage';
 import React, { useState, memo } from 'react';
+import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+
 
 import GenericTable from '../../components/GenericTable';
 import { useRoute } from '../../contexts/RouterContext';
 import { useTranslation } from '../../contexts/TranslationContext';
 import { useResizeInlineBreakpoint } from '../../hooks/useResizeInlineBreakpoint';
 import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental';
+import DeleteWarningModal from '../DeleteWarningModal';
+import { useSetModal } from '../../contexts/ModalContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { useMethod } from '../../contexts/ServerContext';
+
+export function RemoveCustomFieldButton({ _id, reload }) {
+	const removeChat = useMethod('livechat:removeCustomField');
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
+
+	const handleRemoveClick = useMutableCallback(async () => {
+		try {
+			await removeChat(_id);
+		} catch (error) {
+			console.log(error);
+		}
+		reload();
+	});
+
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Custom_Field_Removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
+}
+
 
 const CustomFieldsRow = memo(function CustomFieldsRow({
 	medium,
-	onDelete = () => {},
+	reload,
 	...props
 }) {
 	const {
@@ -19,7 +63,6 @@ const CustomFieldsRow = memo(function CustomFieldsRow({
 		visibility,
 	} = props;
 
-	const t = useTranslation();
 
 	const cfRoute = useRoute('omnichannel-customfields');
 
@@ -63,9 +106,7 @@ const CustomFieldsRow = memo(function CustomFieldsRow({
 			{visibility}
 		</Table.Cell>
 		<Table.Cell withTruncatedText onClick={preventClickPropagation}>
-			<Button small primary danger onClick={onDelete} title={t('Delete')}>
-				<Icon name='trash' size='x16'/>
-			</Button>
+			<RemoveCustomFieldButton _id={_id} reload={reload}/>
 		</Table.Cell>
 	</Table.Row>;
 });
@@ -74,7 +115,7 @@ const CustomFieldsTableContainer = () => {
 	const t = useTranslation();
 	const [params, setParams] = useState(() => ({ current: 0, itemsPerPage: 25 }));
 
-	const { data, state } = useEndpointDataExperimental(`livechat/custom-fields?count=${ params.itemsPerPage }&offset=${ params.current }`);
+	const { data, state, reload } = useEndpointDataExperimental(`livechat/custom-fields?count=${ params.itemsPerPage }&offset=${ params.current }`);
 
 	if (state === ENDPOINT_STATES.ERROR) {
 		return <Callout>
@@ -87,10 +128,11 @@ const CustomFieldsTableContainer = () => {
 		totalCustomFields={data?.total}
 		params={params}
 		onChangeParams={setParams}
+		reload={reload}
 	/>;
 };
 
-export function CustomFieldsTable({ customFields, totalCustomFields, params, onChangeParams }) {
+export function CustomFieldsTable({ customFields, totalCustomFields, params, onChangeParams, reload }) {
 	const t = useTranslation();
 
 	const [ref, onMediumBreakpoint] = useResizeInlineBreakpoint([600], 200);
@@ -117,7 +159,7 @@ export function CustomFieldsTable({ customFields, totalCustomFields, params, onC
 		params={params}
 		setParams={onChangeParams}
 	>
-		{(props) => <CustomFieldsRow key={props._id} medium={onMediumBreakpoint} {...props} />}
+		{(props) => <CustomFieldsRow key={props._id} medium={onMediumBreakpoint} reload={reload} {...props} />}
 	</GenericTable>;
 }
 
diff --git a/client/omnichannel/managers/ManagersRoute.js b/client/omnichannel/managers/ManagersRoute.js
index ae175bedafcd35099958f9ad9f5a3e152bdffc31..273eb2cacc9467e9854e2ed9dcc60c0497c72e16 100644
--- a/client/omnichannel/managers/ManagersRoute.js
+++ b/client/omnichannel/managers/ManagersRoute.js
@@ -1,7 +1,7 @@
 
 import { useDebouncedValue, useMediaQuery, useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useMemo, useCallback, useState } from 'react';
-import { Box, Table, Icon } from '@rocket.chat/fuselage';
+import { Box, Table, Icon, Button } from '@rocket.chat/fuselage';
 
 import { Th } from '../../components/GenericTable';
 import { useTranslation } from '../../contexts/TranslationContext';
@@ -13,6 +13,7 @@ import ManagersPage from './ManagersPage';
 import UserAvatar from '../../components/basic/avatar/UserAvatar';
 
 export function RemoveManagerButton({ _id, reload }) {
+	const t = useTranslation();
 	const deleteAction = useEndpointAction('DELETE', `livechat/users/manager/${ _id }`);
 
 	const handleRemoveClick = useMutableCallback(async () => {
@@ -21,7 +22,11 @@ export function RemoveManagerButton({ _id, reload }) {
 			reload();
 		}
 	});
-	return <Table.Cell fontScale='p1' clickable={true} color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
+	return <Table.Cell fontScale='p1' clickable={true} color='hint' onClick={handleRemoveClick} withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleRemoveClick}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
 }
 
 const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);
diff --git a/client/omnichannel/triggers/TriggersTable.js b/client/omnichannel/triggers/TriggersTable.js
index 8e668f9d0172618b4ce04d171241ea6057380fe5..cdf8c7e2891f876e46bc30cb3f4d4b8b47d53d4d 100644
--- a/client/omnichannel/triggers/TriggersTable.js
+++ b/client/omnichannel/triggers/TriggersTable.js
@@ -1,5 +1,5 @@
-import { Table, Callout, Icon, Button } from '@rocket.chat/fuselage';
-import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
+import { Table, Callout, Icon } from '@rocket.chat/fuselage';
+import { useMutableCallback, Button } from '@rocket.chat/fuselage-hooks';
 import React, { useState, memo, useMemo } from 'react';
 
 import GenericTable from '../../components/GenericTable';
@@ -79,8 +79,8 @@ const TriggersRow = memo(function TriggersRow(props) {
 			{enabled ? t('Yes') : t('No')}
 		</Table.Cell>
 		<Table.Cell withTruncatedText>
-			<Button onClick={handleDelete} small>
-				<Icon name='trash' size='x16' />
+			<Button small ghost title={t('Remove')} onClick={handleDelete}>
+				<Icon name='trash' size='x16'/>
 			</Button>
 		</Table.Cell>
 	</Table.Row>;
@@ -131,7 +131,10 @@ export function TriggersTable({ triggers, totalTriggers, params, onChangeParams,
 			<GenericTable.HeaderCell>
 				{t('Enabled')}
 			</GenericTable.HeaderCell>
-			<GenericTable.HeaderCell width='x60'/>
+			<GenericTable.HeaderCell width='x60'>
+				{t('Remove')}
+			</GenericTable.HeaderCell>
+
 		</>}
 		results={triggers}
 		total={totalTriggers}
diff --git a/ee/client/omnichannel/BusinessHoursTable.js b/ee/client/omnichannel/BusinessHoursTable.js
index 511f33f37a796f6794dd947debebaccc6d8f4605..86baa7e377bbc175f1ded9f42a0a0d8d847ceaf9 100644
--- a/ee/client/omnichannel/BusinessHoursTable.js
+++ b/ee/client/omnichannel/BusinessHoursTable.js
@@ -1,4 +1,4 @@
-import { Table, Callout, Box, TextInput, Icon } from '@rocket.chat/fuselage';
+import { Table, Callout, Box, TextInput, Icon, Button } from '@rocket.chat/fuselage';
 import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useState, memo, useMemo, useEffect } from 'react';
 
@@ -7,6 +7,48 @@ import { useRoute } from '../../../client/contexts/RouterContext';
 import { useTranslation } from '../../../client/contexts/TranslationContext';
 import { useResizeInlineBreakpoint } from '../../../client/hooks/useResizeInlineBreakpoint';
 import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../client/hooks/useEndpointDataExperimental';
+import DeleteWarningModal from '../../../client/omnichannel/DeleteWarningModal';
+import { useSetModal } from '../../../client/contexts/ModalContext';
+import { useToastMessageDispatch } from '../../../client/contexts/ToastMessagesContext';
+import { useMethod } from '../../../client/contexts/ServerContext';
+
+
+export function RemoveBusinessHourButton({ _id, type, reload }) {
+	const removeBusinessHour = useMethod('livechat:removeBusinessHour');
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
+
+	const handleRemoveClick = useMutableCallback(async () => {
+		try {
+			await removeBusinessHour(_id, type);
+		} catch (error) {
+			console.log(error);
+		}
+		reload();
+	});
+
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onBusinessHour = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Business_Hour_Removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onBusinessHour} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
+}
 
 const FilterByText = memo(({ setFilter, ...props }) => {
 	const t = useTranslation();
@@ -32,6 +74,7 @@ const BusinessHoursRow = memo(function BusinessHoursRow(props) {
 		workHours,
 		active,
 		type,
+		reload,
 	} = props;
 
 	const t = useTranslation();
@@ -61,6 +104,10 @@ const BusinessHoursRow = memo(function BusinessHoursRow(props) {
 		return acc;
 	}, []), [t, workHours]);
 
+	const preventClickPropagation = (e) => {
+		e.stopPropagation();
+	};
+
 	return <Table.Row
 		key={_id}
 		role='link'
@@ -81,6 +128,9 @@ const BusinessHoursRow = memo(function BusinessHoursRow(props) {
 		<Table.Cell withTruncatedText>
 			{active ? t('Yes') : t('No')}
 		</Table.Cell>
+		{name && <Table.Cell withTruncatedText onClick={preventClickPropagation}>
+			<RemoveBusinessHourButton _id={_id} reload={reload} type={type}/>
+		</Table.Cell>}
 	</Table.Row>;
 });
 
@@ -88,7 +138,7 @@ const BusinessHoursTableContainer = () => {
 	const t = useTranslation();
 	const [params, setParams] = useState(() => ({ current: 0, itemsPerPage: 25, text: '' }));
 
-	const { data, state } = useEndpointDataExperimental(`livechat/business-hours.list?count=${ params.itemsPerPage }&offset=${ params.current }&name=${ params.text }`);
+	const { data, state, reload } = useEndpointDataExperimental(`livechat/business-hours.list?count=${ params.itemsPerPage }&offset=${ params.current }&name=${ params.text }`);
 
 	if (state === ENDPOINT_STATES.ERROR) {
 		return <Callout>
@@ -101,10 +151,11 @@ const BusinessHoursTableContainer = () => {
 		totalbusinessHours={data?.total}
 		params={params}
 		onChangeParams={setParams}
+		reload={reload}
 	/>;
 };
 
-export function BusinessHoursTable({ businessHours, totalbusinessHours, params, onChangeParams }) {
+export function BusinessHoursTable({ businessHours, totalbusinessHours, params, onChangeParams, reload }) {
 	const t = useTranslation();
 
 	const [ref, onMediumBreakpoint] = useResizeInlineBreakpoint([600], 200);
@@ -124,6 +175,9 @@ export function BusinessHoursTable({ businessHours, totalbusinessHours, params,
 			<GenericTable.HeaderCell width='x100'>
 				{t('Enabled')}
 			</GenericTable.HeaderCell>
+			<GenericTable.HeaderCell width='x100'>
+				{t('Remove')}
+			</GenericTable.HeaderCell>
 		</>}
 		results={businessHours}
 		total={totalbusinessHours}
@@ -131,7 +185,7 @@ export function BusinessHoursTable({ businessHours, totalbusinessHours, params,
 		setParams={onChangeParams}
 		FilterComponent={FilterByText}
 	>
-		{(props) => <BusinessHoursRow key={props._id} medium={onMediumBreakpoint} {...props} />}
+		{(props) => <BusinessHoursRow key={props._id} medium={onMediumBreakpoint} reload={reload} {...props} />}
 	</GenericTable>;
 }
 
diff --git a/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js b/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js
index 437c3066bd2b863348af93105da9ad07474ffc2c..6f9e23ecadb76ccf6abed68e16ac6eb743788a28 100644
--- a/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js
+++ b/ee/client/omnichannel/additionalForms/BusinessHoursMultiple.js
@@ -66,7 +66,7 @@ export const BusinessHoursMultiple = ({ values = {}, handlers = {}, className, d
 		<Field className={className}>
 			<Field.Label>{t('Departments')}</Field.Label>
 			<Field.Row>
-				<MultiSelectFiltered options={departmentList} value={departments} onChange={handleDepartments} placeholder={t('Departments')}/>
+				<MultiSelectFiltered options={departmentList} value={departments} onChange={handleDepartments} maxWidth='100%' placeholder={t('Departments')}/>
 			</Field.Row>
 		</Field>
 	</>;
diff --git a/ee/client/omnichannel/priorities/EditPriority.js b/ee/client/omnichannel/priorities/EditPriority.js
index 018b6aee385b0ff67fb953d3ee3a6dd9cfe04889..62aca4968722e6f5931200175bfd80a193c23100 100644
--- a/ee/client/omnichannel/priorities/EditPriority.js
+++ b/ee/client/omnichannel/priorities/EditPriority.js
@@ -22,7 +22,7 @@ export function PriorityEditWithData({ priorityId, reload }) {
 		return <FormSkeleton/>;
 	}
 
-	if (error) {
+	if (error || !data || !data.priorities) {
 		return <Callout m='x16' type='danger'>{t('Not_Available')}</Callout>;
 	}
 
diff --git a/ee/client/omnichannel/priorities/PrioritiesRoute.js b/ee/client/omnichannel/priorities/PrioritiesRoute.js
index e2026774765b778f760328ae6a969c62e04b1b59..3035dca2d89f80f00b78a1b82ab5ac50eced3542 100644
--- a/ee/client/omnichannel/priorities/PrioritiesRoute.js
+++ b/ee/client/omnichannel/priorities/PrioritiesRoute.js
@@ -2,7 +2,7 @@
 
 import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useMemo, useCallback, useState } from 'react';
-import { Table, Icon } from '@rocket.chat/fuselage';
+import { Table, Icon, Button } from '@rocket.chat/fuselage';
 
 import { Th } from '../../../../client/components/GenericTable';
 import { useTranslation } from '../../../../client/contexts/TranslationContext';
@@ -14,13 +14,18 @@ import { useRouteParameter, useRoute } from '../../../../client/contexts/RouterC
 import VerticalBar from '../../../../client/components/basic/VerticalBar';
 import PrioritiesPage from './PrioritiesPage';
 import { PriorityEditWithData, PriorityNew } from './EditPriority';
+import DeleteWarningModal from '../../../../client/omnichannel/DeleteWarningModal';
+import { useSetModal } from '../../../../client/contexts/ModalContext';
+import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext';
 
 export function RemovePriorityButton({ _id, reload }) {
 	const removePriority = useMethod('livechat:removePriority');
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
 
-	const handleRemoveClick = useMutableCallback(async (e) => {
-		e.preventDefault();
-		e.stopPropagation();
+
+	const handleRemoveClick = useMutableCallback(async () => {
 		try {
 			await removePriority(_id);
 		} catch (error) {
@@ -29,7 +34,26 @@ export function RemovePriorityButton({ _id, reload }) {
 		reload();
 	});
 
-	return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Priority_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
 }
 
 const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);
diff --git a/ee/client/omnichannel/routes.js b/ee/client/omnichannel/routes.js
index e574369c8d5210a8b1ca714c13a70baeeeb80f91..570314b03f7a58ecc343dce7aa1e6207dfb38209 100644
--- a/ee/client/omnichannel/routes.js
+++ b/ee/client/omnichannel/routes.js
@@ -5,7 +5,7 @@ registerOmnichannelRoute('/monitors', {
 	lazyRouteComponent: () => import('./MonitorsPage'),
 });
 
-registerOmnichannelRoute('/priorities', {
+registerOmnichannelRoute('/priorities/:context?/:id?', {
 	name: 'omnichannel-priorities',
 	lazyRouteComponent: () => import('./priorities/PrioritiesRoute'),
 });
diff --git a/ee/client/omnichannel/tags/EditTag.js b/ee/client/omnichannel/tags/EditTag.js
index 9b06ef0a8b7dea3c534c1d9c9b0bbdbf6c29f467..91d6202c6ff6ac2c24f65ab839dedbbf33239112 100644
--- a/ee/client/omnichannel/tags/EditTag.js
+++ b/ee/client/omnichannel/tags/EditTag.js
@@ -114,7 +114,7 @@ export function TagEdit({ data, tagId, isNew, availableDepartments, reload, ...p
 		<Field>
 			<Field.Label>{t('Departments')}</Field.Label>
 			<Field.Row>
-				<MultiSelect options={options} value={departments} error={hasUnsavedChanges && departmentError} placeholder={t('Select_an_option')} onChange={handleDepartments} flexGrow={1}/>
+				<MultiSelect options={options} value={departments} error={hasUnsavedChanges && departmentError} maxWidth='100%' placeholder={t('Select_an_option')} onChange={handleDepartments} flexGrow={1}/>
 			</Field.Row>
 		</Field>
 
diff --git a/ee/client/omnichannel/tags/TagsRoute.js b/ee/client/omnichannel/tags/TagsRoute.js
index 2e80716ec92a3d08c23223fb1b96af5e197b5d3d..3a80935b3e40466ab9bb521015173d5d18331e5b 100644
--- a/ee/client/omnichannel/tags/TagsRoute.js
+++ b/ee/client/omnichannel/tags/TagsRoute.js
@@ -2,7 +2,7 @@
 
 import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useMemo, useCallback, useState } from 'react';
-import { Table, Icon } from '@rocket.chat/fuselage';
+import { Table, Icon, Button } from '@rocket.chat/fuselage';
 
 import { Th } from '../../../../client/components/GenericTable';
 import { useTranslation } from '../../../../client/contexts/TranslationContext';
@@ -14,15 +14,18 @@ import { useRouteParameter, useRoute } from '../../../../client/contexts/RouterC
 import VerticalBar from '../../../../client/components/basic/VerticalBar';
 import TagsPage from './TagsPage';
 import { TagEditWithData, TagNew } from './EditTag';
+import DeleteWarningModal from '../../../../client/omnichannel/DeleteWarningModal';
+import { useSetModal } from '../../../../client/contexts/ModalContext';
+import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext';
 
 export function RemoveTagButton({ _id, reload }) {
 	const removeTag = useMethod('livechat:removeTag');
 	const tagsRoute = useRoute('omnichannel-tags');
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
 
-
-	const handleRemoveClick = useMutableCallback(async (e) => {
-		e.preventDefault();
-		e.stopPropagation();
+	const handleRemoveClick = useMutableCallback(async () => {
 		try {
 			await removeTag(_id);
 		} catch (error) {
@@ -32,7 +35,26 @@ export function RemoveTagButton({ _id, reload }) {
 		reload();
 	});
 
-	return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Tag_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint' onClick={handleDelete} withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
 }
 
 const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);
diff --git a/ee/client/omnichannel/units/EditUnit.js b/ee/client/omnichannel/units/EditUnit.js
index cb073d0e1b938acd630eb42ae805826443b5f2c8..d932cb2e949bcaf078a23eac365e6b99626e915e 100644
--- a/ee/client/omnichannel/units/EditUnit.js
+++ b/ee/client/omnichannel/units/EditUnit.js
@@ -65,13 +65,14 @@ export function UnitEdit({ data, unitId, isNew, availableDepartments, availableM
 	const monOptions = useMemo(() => (availableMonitors && availableMonitors.monitors ? availableMonitors.monitors.map(({ _id, name }) => [_id, name || _id]) : []), [availableMonitors]);
 	const currUnitMonitors = useMemo(() => (unitMonitors && unitMonitors.monitors ? unitMonitors.monitors.map(({ monitorId }) => monitorId) : []), [unitMonitors]);
 	const visibilityOpts = [['public', t('Public')], ['private', t('Private')]];
-	const unitDepartments = useMemo(() => (availableDepartments && availableDepartments.departments ? availableDepartments.departments.map((department) => {
+	const unitDepartments = useMemo(() => (availableDepartments && (availableDepartments.departments && unitId) ? availableDepartments.departments.map((department) => {
 		let result;
 		if (department.parentId === unitId) {
 			result = department._id;
 		}
 		return result;
-	}) : []), [availableDepartments, unitId]);
+	}).filter((department) => !!department) : []), [availableDepartments, unitId]);
+
 
 	const { values, handlers, hasUnsavedChanges } = useForm({ name: unit.name, visibility: unit.visibility, departments: unitDepartments, monitors: currUnitMonitors });
 
@@ -141,13 +142,13 @@ export function UnitEdit({ data, unitId, isNew, availableDepartments, availableM
 		<Field>
 			<Field.Label>{t('Departments')}</Field.Label>
 			<Field.Row>
-				<MultiSelect options={depOptions} value={departments} error={hasUnsavedChanges && departmentError} placeholder={t('Select_an_option')} onChange={handleDepartments} flexGrow={1}/>
+				<MultiSelect options={depOptions} value={departments} error={hasUnsavedChanges && departmentError} maxWidth='100%' placeholder={t('Select_an_option')} onChange={handleDepartments} flexGrow={1}/>
 			</Field.Row>
 		</Field>
 		<Field>
 			<Field.Label>{t('Monitors')}</Field.Label>
 			<Field.Row>
-				<MultiSelect options={monOptions} value={monitors} error={hasUnsavedChanges && unitMonitorsError} placeholder={t('Select_an_option')} onChange={handleMonitors} flexGrow={1}/>
+				<MultiSelect options={monOptions} value={monitors} error={hasUnsavedChanges && unitMonitorsError} maxWidth='100%' placeholder={t('Select_an_option')} onChange={handleMonitors} flexGrow={1}/>
 			</Field.Row>
 		</Field>
 
diff --git a/ee/client/omnichannel/units/UnitsRoute.js b/ee/client/omnichannel/units/UnitsRoute.js
index 5ecf84af24314b630bc2b2d328a577ca7c4d7b1b..a3863db82692a29bc8015d1919785676156d46f8 100644
--- a/ee/client/omnichannel/units/UnitsRoute.js
+++ b/ee/client/omnichannel/units/UnitsRoute.js
@@ -2,7 +2,7 @@
 
 import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';
 import React, { useMemo, useCallback, useState } from 'react';
-import { Table, Icon } from '@rocket.chat/fuselage';
+import { Table, Icon, Button } from '@rocket.chat/fuselage';
 
 import { Th } from '../../../../client/components/GenericTable';
 import { useTranslation } from '../../../../client/contexts/TranslationContext';
@@ -14,15 +14,19 @@ import { useRouteParameter, useRoute } from '../../../../client/contexts/RouterC
 import VerticalBar from '../../../../client/components/basic/VerticalBar';
 import UnitsPage from './UnitsPage';
 import { UnitEditWithData, UnitNew } from './EditUnit';
+import DeleteWarningModal from '../../../../client/omnichannel/DeleteWarningModal';
+import { useSetModal } from '../../../../client/contexts/ModalContext';
+import { useToastMessageDispatch } from '../../../../client/contexts/ToastMessagesContext';
+
 
 export function RemoveUnitButton({ _id, reload }) {
 	const removeUnit = useMethod('livechat:removeUnit');
 	const unitsRoute = useRoute('omnichannel-units');
+	const setModal = useSetModal();
+	const dispatchToastMessage = useToastMessageDispatch();
+	const t = useTranslation();
 
-
-	const handleRemoveClick = useMutableCallback(async (e) => {
-		e.preventDefault();
-		e.stopPropagation();
+	const handleRemoveClick = useMutableCallback(async () => {
 		try {
 			await removeUnit(_id);
 		} catch (error) {
@@ -32,7 +36,26 @@ export function RemoveUnitButton({ _id, reload }) {
 		reload();
 	});
 
-	return <Table.Cell fontScale='p1' color='hint' onClick={handleRemoveClick} withTruncatedText><Icon name='trash' size='x20'/></Table.Cell>;
+	const handleDelete = useMutableCallback((e) => {
+		e.stopPropagation();
+		const onDeleteAgent = async () => {
+			try {
+				await handleRemoveClick();
+				dispatchToastMessage({ type: 'success', message: t('Unit_removed') });
+			} catch (error) {
+				dispatchToastMessage({ type: 'error', message: error });
+			}
+			setModal();
+		};
+
+		setModal(<DeleteWarningModal onDelete={onDeleteAgent} onCancel={() => setModal()}/>);
+	});
+
+	return <Table.Cell fontScale='p1' color='hint' withTruncatedText>
+		<Button small ghost title={t('Remove')} onClick={handleDelete}>
+			<Icon name='trash' size='x16'/>
+		</Button>
+	</Table.Cell>;
 }
 
 const sortDir = (sortDir) => (sortDir === 'asc' ? 1 : -1);
diff --git a/imports/client/hepburn b/imports/client/hepburn
new file mode 120000
index 0000000000000000000000000000000000000000..f439383d3132e74c41507bc10b54f6cd94514b85
--- /dev/null
+++ b/imports/client/hepburn
@@ -0,0 +1 @@
+../../node_modules/hepburn
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 98b1e6d0d6a07fe723e4f90d3daa3827187491dc..7e556848ee7683284b48774aa76887e4db9bc0f0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5028,9 +5028,9 @@
 			}
 		},
 		"@rocket.chat/apps-engine": {
-			"version": "1.17.0-beta.3629",
-			"resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.17.0-beta.3629.tgz",
-			"integrity": "sha512-pQMIJ1JOlRp5wzBm6LgTWPz6Yob0whExfJJ1XcFaq1JV6FBDg6sIyehMsYpop5z8EWZHBstZZc5duN9ixxDZmw==",
+			"version": "1.17.0",
+			"resolved": "https://registry.npmjs.org/@rocket.chat/apps-engine/-/apps-engine-1.17.0.tgz",
+			"integrity": "sha512-4jsN9RUQR9zvZnOr4IZj9bnZR5/kAuSpKJ3JwqTv/DXBeHRSrCCCX0EDoZh5akmQu6x9tRpAXd1DRJWRfI5/Jw==",
 			"requires": {
 				"adm-zip": "^0.4.9",
 				"cryptiles": "^4.1.3",
@@ -5123,9 +5123,9 @@
 			"integrity": "sha512-V/lq5xdIxx016pKB8kSQuQ+IKLC0ULdkZ72M/DEMU1DqXahWDEJeRiBppOO8YN5/mw5INaf8asWOqxJfIFld0w=="
 		},
 		"@rocket.chat/livechat": {
-			"version": "1.7.0",
-			"resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.7.0.tgz",
-			"integrity": "sha512-CISpxnHuCAPgEhLlbdm7naYlh54r0elkvhRaQgzz/ztuANGlroEsucwZgj50izgGC1udiQiO0eYy7lQp8gTqiA==",
+			"version": "1.7.2",
+			"resolved": "https://registry.npmjs.org/@rocket.chat/livechat/-/livechat-1.7.2.tgz",
+			"integrity": "sha512-CikbwDG2ohvnQbSlQIYBe9YUmIyrBgGYtPUXFAI5J9zp6UFg2S0icO2wAN7O3IJJl/RKOVKyWO+3/L83DypVVw==",
 			"dev": true,
 			"requires": {
 				"@kossnocorp/desvg": "^0.2.0",
@@ -15335,9 +15335,9 @@
 			"integrity": "sha512-lcWy3AXDRJOD7MplwZMmNSRM//kZtJaLz4n6D1P5z9wEmZGBKhJRBIr1Xs9KNQJmdXPblvgffynYji4iylUTcA=="
 		},
 		"date-fns": {
-			"version": "2.15.0",
-			"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz",
-			"integrity": "sha512-ZCPzAMJZn3rNUvvQIMlXhDr4A+Ar07eLeGsGREoWU19a3Pqf5oYa+ccd+B3F6XVtQY6HANMFdOQ8A+ipFnvJdQ==",
+			"version": "2.16.0",
+			"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.0.tgz",
+			"integrity": "sha512-DWTRyfOA85sZ4IiXPHhiRIOs3fW5U6Msrp+gElXARa6EpoQTXPyHQmh7hr+ssw2nx9FtOQWnAMJKgL5vaJqILw==",
 			"dev": true
 		},
 		"date-now": {
@@ -17890,7 +17890,7 @@
 				},
 				"chownr": {
 					"version": "1.1.1",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
 					"dev": true,
 					"optional": true
@@ -17925,7 +17925,7 @@
 				},
 				"debug": {
 					"version": "4.1.1",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
 					"dev": true,
 					"optional": true,
@@ -17956,7 +17956,7 @@
 				},
 				"fs-minipass": {
 					"version": "1.2.5",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==",
 					"dev": true,
 					"optional": true,
@@ -17990,7 +17990,7 @@
 				},
 				"glob": {
 					"version": "7.1.3",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
 					"dev": true,
 					"optional": true,
@@ -18022,7 +18022,7 @@
 				},
 				"ignore-walk": {
 					"version": "3.0.1",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==",
 					"dev": true,
 					"optional": true,
@@ -18043,7 +18043,7 @@
 				},
 				"inherits": {
 					"version": "2.0.3",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
 					"dev": true,
 					"optional": true
@@ -18091,7 +18091,7 @@
 				},
 				"minipass": {
 					"version": "2.3.5",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
 					"dev": true,
 					"optional": true,
@@ -18102,7 +18102,7 @@
 				},
 				"minizlib": {
 					"version": "1.2.1",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==",
 					"dev": true,
 					"optional": true,
@@ -18122,7 +18122,7 @@
 				},
 				"ms": {
 					"version": "2.1.1",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
 					"dev": true,
 					"optional": true
@@ -18136,7 +18136,7 @@
 				},
 				"needle": {
 					"version": "2.3.0",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==",
 					"dev": true,
 					"optional": true,
@@ -18148,7 +18148,7 @@
 				},
 				"node-pre-gyp": {
 					"version": "0.12.0",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==",
 					"dev": true,
 					"optional": true,
@@ -18178,14 +18178,14 @@
 				},
 				"npm-bundled": {
 					"version": "1.0.6",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==",
 					"dev": true,
 					"optional": true
 				},
 				"npm-packlist": {
 					"version": "1.4.1",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==",
 					"dev": true,
 					"optional": true,
@@ -18265,7 +18265,7 @@
 				},
 				"process-nextick-args": {
 					"version": "2.0.0",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
 					"dev": true,
 					"optional": true
@@ -18310,7 +18310,7 @@
 				},
 				"rimraf": {
 					"version": "2.6.3",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
 					"dev": true,
 					"optional": true,
@@ -18341,7 +18341,7 @@
 				},
 				"semver": {
 					"version": "5.7.0",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==",
 					"dev": true,
 					"optional": true
@@ -18401,7 +18401,7 @@
 				},
 				"tar": {
 					"version": "4.4.8",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
 					"dev": true,
 					"optional": true,
@@ -18441,7 +18441,7 @@
 				},
 				"yallist": {
 					"version": "3.0.3",
-					"resolved": false,
+					"resolved": "",
 					"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
 					"dev": true,
 					"optional": true
@@ -27366,9 +27366,9 @@
 			}
 		},
 		"preact": {
-			"version": "10.4.7",
-			"resolved": "https://registry.npmjs.org/preact/-/preact-10.4.7.tgz",
-			"integrity": "sha512-DtnnPbOm7oxW7Sxf5Co+KSIOxo7bGm0vLfJN/wGey7G2sAGKnGP5+bFyE2YIgutMISQl6xFVTsOd6l/Au88VVw==",
+			"version": "10.4.8",
+			"resolved": "https://registry.npmjs.org/preact/-/preact-10.4.8.tgz",
+			"integrity": "sha512-uVLeEAyRsCkUEFhVHlOu17OxcrwC7+hTGZ08kBoLBiGHiZooUZuibQnphgMKftw/rqYntNMyhVCPqQhcyAGHag==",
 			"dev": true
 		},
 		"preact-i18nline": {
diff --git a/package.json b/package.json
index 46e1fa0d730433487f9a8b1b50867a977c98c6a3..14c9f7d6e88221972d4b5385477702f23530f91b 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
 		"@babel/preset-react": "^7.10.4",
 		"@octokit/rest": "^16.43.2",
 		"@rocket.chat/eslint-config": "^0.3.0",
-		"@rocket.chat/livechat": "^1.7.0",
+		"@rocket.chat/livechat": "^1.7.2",
 		"@settlin/spacebars-loader": "^1.0.8",
 		"@storybook/addon-actions": "^5.3.19",
 		"@storybook/addon-knobs": "^5.3.19",
@@ -129,7 +129,7 @@
 		"@nivo/heatmap": "^0.61.0",
 		"@nivo/line": "^0.61.1",
 		"@nivo/pie": "^0.61.1",
-		"@rocket.chat/apps-engine": "1.17.0-beta.3629",
+		"@rocket.chat/apps-engine": "1.17.0",
 		"@rocket.chat/css-in-js": "^0.14.1",
 		"@rocket.chat/fuselage": "^0.14.1",
 		"@rocket.chat/fuselage-hooks": "^0.14.1",
diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json
index 46dadd084ee26558b6dfd44bf714de4ed1dd7742..f6b4b89e037482d909300fb64bc8f57e8f275c17 100644
--- a/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -1132,6 +1132,7 @@
   "Custom_Emoji_Has_Been_Deleted": "The custom emoji has been deleted.",
   "Custom_Emoji_Info": "Custom Emoji Info",
   "Custom_Emoji_Updated_Successfully": "Custom emoji updated successfully",
+  "Custom_Field_Removed": "Custom Field Removed",
   "Custom_Fields": "Custom Fields",
   "Custom_oauth_helper": "When setting up your OAuth Provider, you'll have to inform a Callback URL. Use <pre>%s</pre> .",
   "Custom_oauth_unique_name": "Custom oauth unique name",
@@ -3214,6 +3215,8 @@
   "Send_request_on": "Send Request on",
   "Agent_messages": "Agent Messages",
   "Chat_close": "Chat Close",
+  "Chats_removed": "Chats Removed",
+  "Chat_removed": "Chat Removed",
   "Chat_queued": "Chat Queued",
   "Chat_start": "Chat Start",
   "Chat_taken": "Chat Taken",