diff --git a/app/authorization/server/startup.js b/app/authorization/server/startup.js index ff8f0c09f1542..38e43226168eb 100644 --- a/app/authorization/server/startup.js +++ b/app/authorization/server/startup.js @@ -93,6 +93,7 @@ Meteor.startup(function() { { _id: 'create-invite-links', roles: ['admin', 'owner', 'moderator'] }, { _id: 'view-l-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] }, { _id: 'view-livechat-manager', roles: ['livechat-manager', 'admin'] }, + { _id: 'edit-omnichannel-contact', roles: ['livechat-manager', 'admin', 'livechat-agent'] }, { _id: 'view-livechat-rooms', roles: ['livechat-manager', 'admin'] }, { _id: 'close-livechat-room', roles: ['livechat-agent', 'livechat-manager', 'admin'] }, { _id: 'close-others-livechat-room', roles: ['livechat-manager', 'admin'] }, diff --git a/app/livechat/client/tabBar.ts b/app/livechat/client/tabBar.ts index ad5c7a0992255..0e1240d56a504 100644 --- a/app/livechat/client/tabBar.ts +++ b/app/livechat/client/tabBar.ts @@ -7,7 +7,7 @@ addAction('room-info', { id: 'room-info', title: 'Room_Info', icon: 'info-circled', - template: lazy(() => import('../../../client/omnichannel/directory/chats/contextualBar')), + template: lazy(() => import('../../../client/omnichannel/chats/contextualBar')), order: 0, }); diff --git a/app/ui/client/views/app/room.js b/app/ui/client/views/app/room.js index ac3b8616624c0..87614468e85b7 100644 --- a/app/ui/client/views/app/room.js +++ b/app/ui/client/views/app/room.js @@ -890,7 +890,7 @@ Meteor.startup(() => { let room = Rooms.findOne({ _id: rid }, { fields: { t: 1 } }); - if (room?.t === 'l' && !FlowRouter.getParam('tab')) { + if (room?.t === 'l') { room = Tracker.nonreactive(() => Rooms.findOne({ _id: rid })); roomTypes.getConfig(room.t).openCustomProfileTab(this, room, room.v.username); } diff --git a/client/omnichannel/directory/chats/ChatTab.js b/client/omnichannel/chats/ChatTab.js similarity index 90% rename from client/omnichannel/directory/chats/ChatTab.js rename to client/omnichannel/chats/ChatTab.js index 2a9576363cb8d..07cc92c71c554 100644 --- a/client/omnichannel/directory/chats/ChatTab.js +++ b/client/omnichannel/chats/ChatTab.js @@ -4,13 +4,13 @@ import { Table, Tag, Box } from '@rocket.chat/fuselage'; import moment from 'moment'; import { Meteor } from 'meteor/meteor'; -import { useTranslation } from '../../../contexts/TranslationContext'; -import { useEndpointData } from '../../../hooks/useEndpointData'; -import GenericTable from '../../../components/GenericTable'; -import FilterByText from '../../../components/FilterByText'; -import { usePermission } from '../../../contexts/AuthorizationContext'; -import NotAuthorizedPage from '../../../components/NotAuthorizedPage'; -import { useRoute } from '../../../contexts/RouterContext'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useEndpointData } from '../../hooks/useEndpointData'; +import GenericTable from '../../components/GenericTable'; +import FilterByText from '../../components/FilterByText'; +import { usePermission } from '../../contexts/AuthorizationContext'; +import NotAuthorizedPage from '../../components/NotAuthorizedPage'; +import { useRoute } from '../../contexts/RouterContext'; const useQuery = ({ text, itemsPerPage, current }, [column, direction], userIdLoggedIn) => useMemo(() => ({ diff --git a/client/omnichannel/directory/chats/contextualBar/ChatInfo.js b/client/omnichannel/chats/contextualBar/ChatInfo.js similarity index 83% rename from client/omnichannel/directory/chats/contextualBar/ChatInfo.js rename to client/omnichannel/chats/contextualBar/ChatInfo.js index c2728916a2e50..d1f72fa41047e 100644 --- a/client/omnichannel/directory/chats/contextualBar/ChatInfo.js +++ b/client/omnichannel/chats/contextualBar/ChatInfo.js @@ -5,20 +5,21 @@ import { css } from '@rocket.chat/css-in-js'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import UAParser from 'ua-parser-js'; -import VerticalBar from '../../../../components/VerticalBar'; -import UserCard from '../../../../components/UserCard'; -import { FormSkeleton } from '../../Skeleton'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; -import { useTranslation } from '../../../../contexts/TranslationContext'; -import { useFormatDateAndTime } from '../../../../hooks/useFormatDateAndTime'; -import { useFormatDuration } from '../../../../hooks/useFormatDuration'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; -import { UserStatus } from '../../../../components/UserStatus'; -import { roomTypes } from '../../../../../app/utils/client'; -import { useRoute } from '../../../../contexts/RouterContext'; -import { hasPermission } from '../../../../../app/authorization'; - +import VerticalBar from '../../../components/VerticalBar'; +import UserCard from '../../../components/UserCard'; +import { FormSkeleton } from '../../directory/Skeleton'; +import { useEndpointData } from '../../../hooks/useEndpointData'; +import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext'; +import { useTranslation } from '../../../contexts/TranslationContext'; +import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime'; +import { useFormatDuration } from '../../../hooks/useFormatDuration'; +import { AsyncStatePhase } from '../../../hooks/useAsyncState'; +import UserAvatar from '../../../components/avatar/UserAvatar'; +import { UserStatus } from '../../../components/UserStatus'; +import { roomTypes } from '../../../../app/utils/client'; +import { useRoute } from '../../../contexts/RouterContext'; +import { hasPermission } from '../../../../app/authorization'; +import { useUserSubscription } from '../../../contexts/UserContext'; const wordBreak = css` word-break: break-word; @@ -164,8 +165,11 @@ export function ChatInfo({ id, route }) { const { room: { ts, tags, closedAt, departmentId, v, servedBy, metrics, topic, waitingResponse, responseBy, priorityId, livechatData } } = data || { room: { v: { } } }; const routePath = useRoute(route || 'omnichannel-directory'); const canViewCustomFields = () => hasPermission('view-livechat-room-customfields'); + const subscription = useUserSubscription(id); + const hasGlobalEditRoomPermission = hasPermission('save-others-livechat-room-info'); const visitorId = v?._id; + const dispatchToastMessage = useToastMessageDispatch(); useEffect(() => { if (allCustomFields) { const { customFields: customFieldsAPI } = allCustomFields; @@ -179,15 +183,23 @@ export function ChatInfo({ id, route }) { return false; }; - const onEditClick = useMutableCallback(() => routePath.push( - route ? { - context: 'edit', - id, - } : { - tab: 'chats', - context: 'edit', - id, - })); + const onEditClick = useMutableCallback(() => { + const hasEditAccess = !!subscription || hasGlobalEditRoomPermission; + if (!hasEditAccess) { + return dispatchToastMessage({ type: 'error', message: t('Not_authorized') }); + } + + routePath.push( + route ? { + tab: 'room-info', + context: 'edit', + id, + } : { + tab: 'chats', + context: 'edit', + id, + }); + }); if (state === AsyncStatePhase.LOADING) { diff --git a/client/omnichannel/directory/chats/contextualBar/ChatRoomForm.js b/client/omnichannel/chats/contextualBar/ChatRoomForm.js similarity index 89% rename from client/omnichannel/directory/chats/contextualBar/ChatRoomForm.js rename to client/omnichannel/chats/contextualBar/ChatRoomForm.js index 215d64360f4c0..a2a033510e30e 100644 --- a/client/omnichannel/directory/chats/contextualBar/ChatRoomForm.js +++ b/client/omnichannel/chats/contextualBar/ChatRoomForm.js @@ -3,18 +3,17 @@ import { Field, TextInput, ButtonGroup, Button, Box } from '@rocket.chat/fuselag import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSubscription } from 'use-subscription'; -import { useTranslation } from '../../../../contexts/TranslationContext'; -import VerticalBar from '../../../../components/VerticalBar'; -import { useForm } from '../../../../hooks/useForm'; -import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; -import { FormSkeleton } from '../../Skeleton'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { hasAtLeastOnePermission } from '../../../../../app/authorization'; -import CustomFieldsForm from '../../../../components/CustomFieldsForm'; -import { useMethod } from '../../../../contexts/ServerContext'; -import { formsSubscription } from '../../../../views/omnichannel/additionalForms'; - +import { useTranslation } from '../../../contexts/TranslationContext'; +import VerticalBar from '../../../components/VerticalBar'; +import { useForm } from '../../../hooks/useForm'; +import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext'; +import { useEndpointData } from '../../../hooks/useEndpointData'; +import { FormSkeleton } from '../../directory/Skeleton'; +import { AsyncStatePhase } from '../../../hooks/useAsyncState'; +import { hasAtLeastOnePermission } from '../../../../app/authorization'; +import CustomFieldsForm from '../../../components/CustomFieldsForm'; +import { useMethod } from '../../../contexts/ServerContext'; +import { formsSubscription } from '../../../views/omnichannel/additionalForms'; const initialValuesUser = { name: '', @@ -188,7 +187,7 @@ export function RoomEdit({ room, visitor, reload, close }) { try { saveRoom(userData, roomData); dispatchToastMessage({ type: 'success', message: t('Saved') }); - reload(); + reload && reload(); close(); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); diff --git a/client/omnichannel/directory/chats/contextualBar/index.js b/client/omnichannel/chats/contextualBar/index.js similarity index 53% rename from client/omnichannel/directory/chats/contextualBar/index.js rename to client/omnichannel/chats/contextualBar/index.js index f7ce0b379b020..6b1407d7fdfcd 100644 --- a/client/omnichannel/directory/chats/contextualBar/index.js +++ b/client/omnichannel/chats/contextualBar/index.js @@ -2,25 +2,34 @@ import React from 'react'; import { Icon, Box } from '@rocket.chat/fuselage'; import { ChatInfo } from './ChatInfo'; -import VerticalBar from '../../../../components/VerticalBar'; -import { useRoute } from '../../../../contexts/RouterContext'; -import { useTranslation } from '../../../../contexts/TranslationContext'; +import { RoomEditWithData } from './ChatRoomForm'; +import VerticalBar from '../../../components/VerticalBar'; +import { useRoute, useRouteParameter } from '../../../contexts/RouterContext'; +import { useTranslation } from '../../../contexts/TranslationContext'; const PATH = 'live'; + const ChatsContextualBar = ({ id }) => { const t = useTranslation(); + const context = useRouteParameter('context'); + const directoryRoute = useRoute(PATH); const closeContextualBar = () => { directoryRoute.push({ id }); }; + + const handleRoomEditBarCloseButtonClick = () => { + directoryRoute.push({ id, tab: 'room-info' }); + }; + return <> {t('Room_Info')} - + {context === 'edit' ? : } ; }; diff --git a/client/omnichannel/directory/contacts/ContactTab.js b/client/omnichannel/contacts/ContactTab.js similarity index 87% rename from client/omnichannel/directory/contacts/ContactTab.js rename to client/omnichannel/contacts/ContactTab.js index 35425777674f5..590c92ea3bc91 100644 --- a/client/omnichannel/directory/contacts/ContactTab.js +++ b/client/omnichannel/contacts/ContactTab.js @@ -2,14 +2,14 @@ import React, { useState, useMemo, useCallback, useEffect } from 'react'; import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { Table } from '@rocket.chat/fuselage'; -import { useTranslation } from '../../../contexts/TranslationContext'; -import { useEndpointData } from '../../../hooks/useEndpointData'; -import GenericTable from '../../../components/GenericTable'; -import FilterByText from '../../../components/FilterByText'; -import { useRoute } from '../../../contexts/RouterContext'; -import { useFormatDate } from '../../../hooks/useFormatDate'; -import { usePermission } from '../../../contexts/AuthorizationContext'; -import { NotAuthorizedPage } from '../../../components/NotAuthorizedPage'; +import { useTranslation } from '../../contexts/TranslationContext'; +import { useEndpointData } from '../../hooks/useEndpointData'; +import GenericTable from '../../components/GenericTable'; +import FilterByText from '../../components/FilterByText'; +import { useRoute } from '../../contexts/RouterContext'; +import { useFormatDate } from '../../hooks/useFormatDate'; +import { usePermission } from '../../contexts/AuthorizationContext'; +import { NotAuthorizedPage } from '../../components/NotAuthorizedPage'; const useQuery = ({ text, itemsPerPage, current }, [column, direction]) => useMemo(() => ({ diff --git a/client/omnichannel/directory/contacts/contextualBar/ContactForm.js b/client/omnichannel/contacts/contextualBar/ContactForm.js similarity index 87% rename from client/omnichannel/directory/contacts/contextualBar/ContactForm.js rename to client/omnichannel/contacts/contextualBar/ContactForm.js index 432148adc934a..0b10d1f7dba70 100644 --- a/client/omnichannel/directory/contacts/contextualBar/ContactForm.js +++ b/client/omnichannel/contacts/contextualBar/ContactForm.js @@ -3,20 +3,20 @@ import { Field, TextInput, ButtonGroup, Button, Box } from '@rocket.chat/fuselag import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useSubscription } from 'use-subscription'; -import { useTranslation } from '../../../../contexts/TranslationContext'; -import VerticalBar from '../../../../components/VerticalBar'; -import { useForm } from '../../../../hooks/useForm'; -import { isEmail } from '../../../../../app/utils'; -import { useComponentDidUpdate } from '../../../../hooks/useComponentDidUpdate'; -import { useEndpointAction } from '../../../../hooks/useEndpointAction'; -import { useToastMessageDispatch } from '../../../../contexts/ToastMessagesContext'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; -import { FormSkeleton } from '../../Skeleton'; -import CustomFieldsForm from '../../../../components/CustomFieldsForm'; -import { hasAtLeastOnePermission } from '../../../../../app/authorization'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { formsSubscription } from '../../../../views/omnichannel/additionalForms'; -import { createToken } from '../../../../components/helpers'; +import { useTranslation } from '../../../contexts/TranslationContext'; +import VerticalBar from '../../../components/VerticalBar'; +import { useForm } from '../../../hooks/useForm'; +import { isEmail } from '../../../../app/utils'; +import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate'; +import { useEndpointAction } from '../../../hooks/useEndpointAction'; +import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext'; +import { useEndpointData } from '../../../hooks/useEndpointData'; +import { FormSkeleton } from '../../directory/Skeleton'; +import CustomFieldsForm from '../../../components/CustomFieldsForm'; +import { hasAtLeastOnePermission } from '../../../../app/authorization'; +import { AsyncStatePhase } from '../../../hooks/useAsyncState'; +import { formsSubscription } from '../../../views/omnichannel/additionalForms'; +import { createToken } from '../../../components/helpers'; const initialValues = { token: '', @@ -38,7 +38,7 @@ const getInitialValues = (data) => { name: name ?? '', email: visitorEmails ? visitorEmails[0].address : '', phone: phone ? phone[0].phoneNumber : '', - livechatData: livechatData ?? '', + livechatData: livechatData ?? {}, username: contactManager?.username ?? '', }; }; @@ -188,7 +188,7 @@ export function ContactNewEdit({ id, data, reload, close }) { try { await saveContact(payload); dispatchToastMessage({ type: 'success', message: t('Saved') }); - reload(); + reload && reload(); close(); } catch (error) { dispatchToastMessage({ type: 'error', message: error }); diff --git a/client/omnichannel/directory/contacts/contextualBar/ContactInfo.js b/client/omnichannel/contacts/contextualBar/ContactInfo.js similarity index 75% rename from client/omnichannel/directory/contacts/contextualBar/ContactInfo.js rename to client/omnichannel/contacts/contextualBar/ContactInfo.js index 0da5b88bb16fe..482d61a9fdc69 100644 --- a/client/omnichannel/directory/contacts/contextualBar/ContactInfo.js +++ b/client/omnichannel/contacts/contextualBar/ContactInfo.js @@ -4,18 +4,19 @@ import { css } from '@rocket.chat/css-in-js'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { FlowRouter } from 'meteor/kadira:flow-router'; -import VerticalBar from '../../../../components/VerticalBar'; -import UserCard from '../../../../components/UserCard'; -import { FormSkeleton } from '../../Skeleton'; -import { useEndpointData } from '../../../../hooks/useEndpointData'; -import { useTranslation } from '../../../../contexts/TranslationContext'; -import { useRoute } from '../../../../contexts/RouterContext'; -import { hasPermission } from '../../../../../app/authorization'; -import { useFormatDate } from '../../../../hooks/useFormatDate'; -import { AsyncStatePhase } from '../../../../hooks/useAsyncState'; -import { ContactManagerInfo } from '../../../../../ee/client/omnichannel/ContactManager'; -import UserAvatar from '../../../../components/avatar/UserAvatar'; -import { UserStatus } from '../../../../components/UserStatus'; +import VerticalBar from '../../../components/VerticalBar'; +import UserCard from '../../../components/UserCard'; +import { FormSkeleton } from '../../directory/Skeleton'; +import { useEndpointData } from '../../../hooks/useEndpointData'; +import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext'; +import { useTranslation } from '../../../contexts/TranslationContext'; +import { useRoute } from '../../../contexts/RouterContext'; +import { hasPermission } from '../../../../app/authorization'; +import { useFormatDate } from '../../../hooks/useFormatDate'; +import { AsyncStatePhase } from '../../../hooks/useAsyncState'; +import { ContactManagerInfo } from '../../../../ee/client/omnichannel/ContactManager'; +import UserAvatar from '../../../components/avatar/UserAvatar'; +import { UserStatus } from '../../../components/UserStatus'; const wordBreak = css` @@ -40,9 +41,9 @@ const CustomField = ({ id, value }) => { }; -export function ContactInfo({ id }) { +export function ContactInfo({ id, rid, route }) { const t = useTranslation(); - const directoryRoute = useRoute('omnichannel-directory'); + const routePath = useRoute(route || 'omnichannel-directory'); const { value: allCustomFields, phase: stateCustomFields } = useEndpointData('livechat/custom-fields'); @@ -50,14 +51,25 @@ export function ContactInfo({ id }) { const formatDate = useFormatDate(); + const dispatchToastMessage = useToastMessageDispatch(); const canViewCustomFields = () => hasPermission('view-livechat-room-customfields'); - const onEditButtonClick = useMutableCallback(() => directoryRoute.push({ - tab: 'contacts', - context: 'edit', - id, - })); + const onEditButtonClick = useMutableCallback(() => { + if (!hasPermission('edit-omnichannel-contact')) { + return dispatchToastMessage({ type: 'error', message: t('Not_authorized') }); + } + + routePath.push(route ? { + tab: 'contact-profile', + context: 'edit', + id: rid, + } : { + tab: 'contacts', + context: 'edit', + id, + }); + }); useEffect(() => { if (allCustomFields) { diff --git a/client/omnichannel/contacts/contextualBar/index.js b/client/omnichannel/contacts/contextualBar/index.js new file mode 100644 index 0000000000000..9c2b603717dc4 --- /dev/null +++ b/client/omnichannel/contacts/contextualBar/index.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { Icon, Box } from '@rocket.chat/fuselage'; + +import VerticalBar from '../../../components/VerticalBar'; +import { useRoute, useRouteParameter } from '../../../contexts/RouterContext'; +import { useTranslation } from '../../../contexts/TranslationContext'; +import { useRoom } from '../../../views/room/providers/RoomProvider'; +import { ContactInfo } from './ContactInfo'; +import { ContactEditWithData } from './ContactForm'; + +const PATH = 'live'; +const ContactsContextualBar = ({ id }) => { + const t = useTranslation(); + + const directoryRoute = useRoute(PATH); + + const context = useRouteParameter('context'); + + const closeContextualBar = () => { + directoryRoute.push({ id }); + }; + + const handleContactEditBarCloseButtonClick = () => { + directoryRoute.push({ id, tab: 'contact-profile' }); + }; + + const room = useRoom(); + + const { v: { _id } } = room; + + return <> + + {t('Contact_Info')} + + + {context === 'edit' ? : } + ; +}; + +export default ({ rid }) => ; diff --git a/client/omnichannel/directory/OmnichannelDirectoryPage.js b/client/omnichannel/directory/OmnichannelDirectoryPage.js index c361539ee7d82..b9a06b30f92ac 100644 --- a/client/omnichannel/directory/OmnichannelDirectoryPage.js +++ b/client/omnichannel/directory/OmnichannelDirectoryPage.js @@ -5,13 +5,13 @@ import { FlowRouter } from 'meteor/kadira:flow-router'; import { useTranslation } from '../../contexts/TranslationContext'; import Page from '../../components/Page'; import { useRoute, useRouteParameter } from '../../contexts/RouterContext'; -import ContactTab from './contacts/ContactTab'; +import ContactTab from '../contacts/ContactTab'; import VerticalBar from '../../components/VerticalBar'; -import { ContactNewEdit, ContactEditWithData } from './contacts/contextualBar/ContactForm'; -import { ContactInfo } from './contacts/contextualBar/ContactInfo'; -import ChatTab from './chats/ChatTab'; -import { ChatInfo } from './chats/contextualBar/ChatInfo'; -import { RoomEditWithData } from './chats/contextualBar/ChatRoomForm'; +import { ContactNewEdit, ContactEditWithData } from '../contacts/contextualBar/ContactForm'; +import { ContactInfo } from '../contacts/contextualBar/ContactInfo'; +import ChatTab from '../chats/ChatTab'; +import { ChatInfo } from '../chats/contextualBar/ChatInfo'; +import { RoomEditWithData } from '../chats/contextualBar/ChatRoomForm'; const OmnichannelDirectoryPage = () => { diff --git a/client/omnichannel/directory/contacts/contextualBar/index.js b/client/omnichannel/directory/contacts/contextualBar/index.js deleted file mode 100644 index 36e21c970177f..0000000000000 --- a/client/omnichannel/directory/contacts/contextualBar/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { Icon, Box } from '@rocket.chat/fuselage'; - -import VerticalBar from '../../../../components/VerticalBar'; -import { useRoute } from '../../../../contexts/RouterContext'; -import { useTranslation } from '../../../../contexts/TranslationContext'; -import { useRoom } from '../../../../views/room/providers/RoomProvider'; -import { ContactInfo } from './ContactInfo'; - -const ContactsContextualBar = ({ id }) => { - const t = useTranslation(); - - const directoryRoute = useRoute('live'); - - const closeContextualBar = () => { - directoryRoute.push({ id }); - }; - const room = useRoom(); - - const { v: { _id } } = room; - - return <> - - {t('Contact_Info')} - - - - ; -}; - -export default ({ rid }) => ; diff --git a/client/views/room/lib/Toolbox/defaultActions.ts b/client/views/room/lib/Toolbox/defaultActions.ts index 966fc710dde06..1f6686524074e 100644 --- a/client/views/room/lib/Toolbox/defaultActions.ts +++ b/client/views/room/lib/Toolbox/defaultActions.ts @@ -28,7 +28,7 @@ addAction('contact-profile', { id: 'contact-profile', title: 'Contact_Info', icon: 'user', - template: lazy(() => import('../../../../omnichannel/directory/contacts/contextualBar')), + template: lazy(() => import('../../../../omnichannel/contacts/contextualBar')), order: 5, }); diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 2163aef00c8db..9d34b7041e9e8 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1481,6 +1481,8 @@ "edit-room-avatar_description": "Permission to edit a room's avatar.", "edit-room-retention-policy": "Edit Room's Retention Policy", "edit-room-retention-policy_description": "Permission to edit a room’s retention policy, to automatically delete messages in it", + "edit-omnichannel-contact": "Edit Omnichannel Contact", + "edit-omnichannel-contact_description": "Permission to edit Omnichannel Contact", "Edit_Contact_Profile": "Edit Contact Profile", "edited": "edited", "Editing_room": "Editing room",