diff --git a/app/livechat/client/route.js b/app/livechat/client/route.js index 1097aea99832b..f440147a9fabd 100644 --- a/app/livechat/client/route.js +++ b/app/livechat/client/route.js @@ -53,8 +53,8 @@ AccountBox.addRoute({ name: 'livechat-agents', path: '/agents', sideNav: 'livechatFlex', - i18nPageTitle: 'Livechat_agents', pageTemplate: 'livechatAgents', + customContainer: true, }, livechatManagerRoutes, load); AccountBox.addRoute({ diff --git a/app/livechat/client/views/app/livechatAgents.html b/app/livechat/client/views/app/livechatAgents.html index 7164e017fec59..eb333a2144aa1 100644 --- a/app/livechat/client/views/app/livechatAgents.html +++ b/app/livechat/client/views/app/livechatAgents.html @@ -1,89 +1,99 @@ + + {{#with flexData}} + {{> flexTabBar}} + {{/with}} + + {{/requiresPermission}} + diff --git a/app/livechat/client/views/app/livechatAgents.js b/app/livechat/client/views/app/livechatAgents.js index 46e7270484a80..f153fbc88321a 100644 --- a/app/livechat/client/views/app/livechatAgents.js +++ b/app/livechat/client/views/app/livechatAgents.js @@ -1,10 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { Template } from 'meteor/templating'; +import { FlowRouter } from 'meteor/kadira:flow-router'; import { ReactiveVar } from 'meteor/reactive-var'; import { ReactiveDict } from 'meteor/reactive-dict'; import _ from 'underscore'; -import { modal, call } from '../../../../ui-utils'; +import { modal, call, TabBar, RocketChatTabBar } from '../../../../ui-utils'; import { t, handleError, APIClient } from '../../../../utils/client'; import './livechatAgents.html'; @@ -83,6 +84,12 @@ Template.livechatAgents.helpers({ } }; }, + flexData() { + return { + tabBar: Template.instance().tabBar, + data: Template.instance().tabBarData.get(), + }; + }, }); const DEBOUNCE_TIME_FOR_SEARCH_AGENTS_IN_MS = 300; @@ -109,7 +116,13 @@ Template.livechatAgents.events({ if (error) { return handleError(error); } + + if (instance.tabBar.getState() === 'opened') { + instance.tabBar.close(); + } + await loadAgents(instance); + modal.open({ title: t('Removed'), text: t('Agent_removed'), @@ -144,23 +157,30 @@ Template.livechatAgents.events({ state.set('loading', false); } }, + 'keydown #agents-filter'(e) { if (e.which === 13) { e.stopPropagation(); e.preventDefault(); } }, + 'keyup #agents-filter': _.debounce((e, t) => { - e.stopPropagation(); e.preventDefault(); t.filter.set(e.currentTarget.value); }, DEBOUNCE_TIME_FOR_SEARCH_AGENTS_IN_MS), - /* - 'click .agent-info'(e, instance) { + + 'click .user-info'(e, instance) { e.preventDefault(); - instance.tabBarData.set(FullUser.findOne(this._id)); - instance.tabBar.open('admin-user-info'); + instance.tabBarData.set({ + agentId: this._id, + onRemoveAgent: () => loadAgents(instance), + }); + + instance.tabBar.setData({ label: t('Agent_Info'), icon: 'livechat' }); + instance.tabBar.open('livechat-agent-info'); }, + /* 'click .info-tabs button'(e) { e.preventDefault(); $('.info-tabs button').removeClass('active'); @@ -181,6 +201,18 @@ Template.livechatAgents.onCreated(function() { this.ready = new ReactiveVar(true); this.selectedAgents = new ReactiveVar([]); this.agents = new ReactiveVar([]); + this.tabBar = new RocketChatTabBar(); + this.tabBar.showGroup(FlowRouter.current().route.name); + this.tabBarData = new ReactiveVar(); + + TabBar.addButton({ + groups: ['livechat-agent-users'], + id: 'livechat-agent-info', + i18nTitle: 'Agent_Info', + icon: 'livechat', + template: 'agentInfo', + order: 1, + }); this.onSelectAgents = ({ item: agent }) => { this.selectedAgents.set([...this.selectedAgents.curValue, agent]); diff --git a/app/livechat/client/views/app/tabbar/agentEdit.html b/app/livechat/client/views/app/tabbar/agentEdit.html new file mode 100644 index 0000000000000..2fa2438368c85 --- /dev/null +++ b/app/livechat/client/views/app/tabbar/agentEdit.html @@ -0,0 +1,97 @@ + diff --git a/app/livechat/client/views/app/tabbar/agentEdit.js b/app/livechat/client/views/app/tabbar/agentEdit.js new file mode 100644 index 0000000000000..a8f3e0a075a11 --- /dev/null +++ b/app/livechat/client/views/app/tabbar/agentEdit.js @@ -0,0 +1,156 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Template } from 'meteor/templating'; +import toastr from 'toastr'; + +import { getCustomFormTemplate } from '../customTemplates/register'; +import './agentEdit.html'; +import { LivechatDepartmentAgents } from '../../../collections/LivechatDepartmentAgents'; +import { hasPermission } from '../../../../../authorization'; +import { t, handleError, APIClient } from '../../../../../utils/client'; + +Template.agentEdit.helpers({ + canEditDepartment() { + return hasPermission('add-livechat-department-agents'); + }, + + agent() { + return Template.instance().agent.get(); + }, + + availableDepartments() { + return Template.instance().availableDepartments.get(); + }, + + hasAvailableDepartments() { + if (!hasPermission('add-livechat-department-agents')) { + return; + } + + const availableDepartments = [...Template.instance().availableDepartments.get()]; + return availableDepartments.length > 0; + }, + + agentDepartments() { + const deptIds = Template.instance().agentDepartments.get(); + const departments = Template.instance().departments.get(); + return departments.filter(({ _id }) => deptIds.includes(_id)); + }, + + hasAgentDepartments() { + const agentDepartments = [...Template.instance().agentDepartments.get()]; + return agentDepartments.length > 0; + }, + + customFieldsTemplate() { + return getCustomFormTemplate('livechatAgentEditForm'); + }, + + agentDataContext() { + // To make the dynamic template reactive we need to pass a ReactiveVar through the data property + // because only the dynamic template data will be reloaded + return Template.instance().agent; + }, + + isReady() { + const instance = Template.instance(); + return instance.ready && instance.ready.get(); + }, +}); + +Template.agentEdit.events({ + 'click .cancel'(e) { + e.stopPropagation(); + e.preventDefault(); + + return this.back && this.back(); + }, + + 'submit #agent-form'(e, instance) { + e.preventDefault(); + const _id = $(e.currentTarget).data('id'); + + const agentData = {}; + instance.$('.customFormField').each((i, el) => { + const elField = instance.$(el); + const name = elField.attr('name'); + agentData[name] = elField.val(); + }); + + const agentDepartments = instance.agentDepartments.get(); + Meteor.call('livechat:saveAgentInfo', _id, agentData, agentDepartments, (error) => { + if (error) { + return handleError(error); + } + + toastr.success(t('Saved')); + return this.back && this.back(_id); + }); + }, + + 'click .remove-department'(e, instance) { + e.stopPropagation(); + e.preventDefault(); + + if (!hasPermission('add-livechat-department-agents')) { + return; + } + + const { currentTarget: { dataset: { id } } } = e; + const agentDepartments = instance.agentDepartments.get(); + instance.agentDepartments.set(agentDepartments.filter((el) => el !== id)); + }, + + 'click #addDepartment'(e, instance) { + e.stopPropagation(); + e.preventDefault(); + + if ($('#departmentSelect').find(':selected').is(':disabled')) { + return; + } + + const agentDepartments = [...instance.agentDepartments.get()]; + const deptVal = $('#departmentSelect').val(); + if (deptVal === '' || agentDepartments.indexOf(deptVal) > -1) { + return; + } + + agentDepartments.push(deptVal); + instance.agentDepartments.set(agentDepartments); + $('#departmentSelect').val('placeholder'); + }, +}); + +Template.agentEdit.onCreated(async function() { + this.agent = new ReactiveVar(); + this.ready = new ReactiveVar(false); + this.agentDepartments = new ReactiveVar([]); + this.departments = new ReactiveVar([]); + this.availableDepartments = new ReactiveVar([]); + this.back = Template.currentData().back; + + const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}'); + this.departments.set(departments); + this.availableDepartments.set(departments.filter(({ enabled }) => enabled)); + + this.autorun(async () => { + this.ready.set(false); + + const { agentId } = Template.currentData(); + + if (!agentId) { + return; + } + + const { user } = await APIClient.v1.get(`livechat/users/agent/${ agentId }`); + + this.agent.set(user); + + Tracker.nonreactive(() => this.subscribe('livechat:departmentAgents', null, agentId, () => { + this.agentDepartments.set(LivechatDepartmentAgents.find({ agentId }).map((deptAgent) => deptAgent.departmentId)); + })); + + this.ready.set(true); + }); +}); diff --git a/app/livechat/client/views/app/tabbar/agentInfo.html b/app/livechat/client/views/app/tabbar/agentInfo.html new file mode 100644 index 0000000000000..c3c1cac243845 --- /dev/null +++ b/app/livechat/client/views/app/tabbar/agentInfo.html @@ -0,0 +1,63 @@ + diff --git a/app/livechat/client/views/app/tabbar/agentInfo.js b/app/livechat/client/views/app/tabbar/agentInfo.js new file mode 100644 index 0000000000000..2c02bf856f0f0 --- /dev/null +++ b/app/livechat/client/views/app/tabbar/agentInfo.js @@ -0,0 +1,186 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import { ReactiveVar } from 'meteor/reactive-var'; +import { Session } from 'meteor/session'; +import { Template } from 'meteor/templating'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import _ from 'underscore'; +import s from 'underscore.string'; + +import { getCustomFormTemplate } from '../customTemplates/register'; +import './agentInfo.html'; +import { modal } from '../../../../../ui-utils'; +import { t, handleError, APIClient } from '../../../../../utils/client'; +import { hasPermission } from '../../../../../authorization'; +import { LivechatDepartmentAgents } from '../../../collections/LivechatDepartmentAgents'; + +const customFieldsTemplate = () => getCustomFormTemplate('livechatAgentInfoForm'); + +Template.agentInfo.helpers({ + canEdit() { + const availableDepartments = [...Template.instance().availableDepartments.get()]; + const hasCustomFields = customFieldsTemplate() !== null; + return (availableDepartments.length > 0 && hasPermission('add-livechat-department-agents')) || hasCustomFields; + }, + + name() { + const agent = Template.instance().agent.get(); + return agent && agent.name ? agent.name : TAPi18n.__('Unnamed'); + }, + + username() { + const agent = Template.instance().agent.get(); + return agent && agent.username; + }, + + agentStatus() { + const agent = Template.instance().agent.get(); + const userStatus = Session.get(`user_${ agent.username }_status`); + return userStatus || TAPi18n.__('offline'); + }, + + agentStatusText() { + const agent = Template.instance().agent.get(); + if (agent && s.trim(agent.statusText)) { + return agent.statusText; + } + + const agentStatus = Session.get(`user_${ agent.username }_status`); + return agentStatus || TAPi18n.__('offline'); + }, + + email() { + const agent = Template.instance().agent.get(); + return agent && agent.emails && agent.emails[0] && agent.emails[0].address; + }, + + agent() { + return Template.instance().agent.get(); + }, + + hasEmails() { + const agent = Template.instance().agent.get(); + return agent && _.isArray(agent.emails); + }, + + editingAgent() { + return Template.instance().action.get() === 'edit'; + }, + + agentToEdit() { + const instance = Template.instance(); + const agent = instance.agent.get(); + return { + agentId: agent && agent._id, + back(agentId) { + instance.action.set(); + instance.agentEdited.set(agentId); + }, + }; + }, + + agentDepartments() { + const deptIds = Template.instance().agentDepartments.get(); + const departments = Template.instance().departments.get(); + return departments.filter(({ _id }) => deptIds.includes(_id)); + }, + + customFieldsTemplate, + + agentDataContext() { + // To make the dynamic template reactive we need to pass a ReactiveVar through the data property + // because only the dynamic template data will be reloaded + return Template.instance().agent; + }, + + isReady() { + const instance = Template.instance(); + return instance.ready && instance.ready.get(); + }, +}); + +Template.agentInfo.events({ + 'click .delete-agent'(e, instance) { + e.preventDefault(); + + modal.open( + { + title: t('Are_you_sure'), + type: 'warning', + showCancelButton: true, + confirmButtonColor: '#DD6B55', + confirmButtonText: t('Yes'), + cancelButtonText: t('Cancel'), + closeOnConfirm: false, + html: false, + }, + () => { + Meteor.call('livechat:removeAgent', this.username, (error) => { + if (error) { + return handleError(error); + } + + const { tabBar, onRemoveAgent } = instance; + tabBar.close(); + onRemoveAgent && onRemoveAgent(); + + modal.open({ + title: t('Removed'), + text: t('Agent_removed'), + type: 'success', + timer: 1000, + showConfirmButton: false, + }); + }); + } + ); + }, + 'click .edit-agent'(e, instance) { + e.preventDefault(); + instance.action.set('edit'); + }, +}); + +Template.agentInfo.onCreated(async function() { + this.agent = new ReactiveVar(); + this.ready = new ReactiveVar(false); + this.agentEdited = new ReactiveVar(); + this.departments = new ReactiveVar([]); + this.availableDepartments = new ReactiveVar([]); + this.agentDepartments = new ReactiveVar([]); + this.action = new ReactiveVar(); + this.tabBar = Template.currentData().tabBar; + this.onRemoveAgent = Template.currentData().onRemoveAgent; + + const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}'); + this.departments.set(departments); + this.availableDepartments.set(departments.filter(({ enabled }) => enabled)); + + const loadAgentData = async (agentId) => { + this.ready.set(false); + const { user } = await APIClient.v1.get(`livechat/users/agent/${ agentId }`); + this.agent.set(user); + + // TODO: Need to replace the following subscribe by the REST approach + Tracker.nonreactive(() => this.subscribe('livechat:departmentAgents', null, agentId, () => { + this.agentDepartments.set(LivechatDepartmentAgents.find({ agentId }).map((deptAgent) => deptAgent.departmentId)); + })); + + this.ready.set(true); + }; + + this.autorun(() => { + const { agentId } = Template.currentData(); + if (agentId) { + loadAgentData(agentId); + } + }); + + this.autorun(() => { + const agentEdited = this.agentEdited.get(); + if (agentEdited) { + loadAgentData(agentEdited); + this.agentEdited.set(); + } + }); +}); diff --git a/app/livechat/client/views/regular.js b/app/livechat/client/views/regular.js index 24a4f885672eb..e1e1a26cd8d72 100644 --- a/app/livechat/client/views/regular.js +++ b/app/livechat/client/views/regular.js @@ -3,6 +3,8 @@ import './app/livechatQueue'; import './app/livechatReadOnly'; import './app/livechatNotSubscribed.html'; import './app/livechatRoomTagSelector.html'; +import './app/tabbar/agentEdit'; +import './app/tabbar/agentInfo'; import './app/tabbar/externalSearch'; import './app/tabbar/visitorEdit'; import './app/tabbar/visitorForward'; diff --git a/app/livechat/imports/server/rest/users.js b/app/livechat/imports/server/rest/users.js index 6bf082858c4bd..04e1815b4f07f 100644 --- a/app/livechat/imports/server/rest/users.js +++ b/app/livechat/imports/server/rest/users.js @@ -104,7 +104,7 @@ API.v1.addRoute('livechat/users/:type/:_id', { authRequired: true }, { if (user.roles.indexOf(role) !== -1) { return API.v1.success({ - user: _.pick(user, '_id', 'username'), + user: _.pick(user, '_id', 'username', 'name', 'status', 'statusLivechat', 'emails', 'livechat'), }); } diff --git a/app/livechat/server/api/lib/users.js b/app/livechat/server/api/lib/users.js index e630088badb09..57a57b5657f42 100644 --- a/app/livechat/server/api/lib/users.js +++ b/app/livechat/server/api/lib/users.js @@ -20,6 +20,7 @@ async function findUsers({ role, text, pagination: { offset, count, sort } }) { status: 1, statusLivechat: 1, emails: 1, + livechat: 1, }, }); diff --git a/app/livechat/server/index.js b/app/livechat/server/index.js index 458b660e15ad6..9da8309d30f32 100644 --- a/app/livechat/server/index.js +++ b/app/livechat/server/index.js @@ -42,6 +42,7 @@ import './methods/removeDepartment'; import './methods/removeManager'; import './methods/removeTrigger'; import './methods/removeRoom'; +import './methods/saveAgentInfo'; import './methods/saveAppearance'; import './methods/saveCustomField'; import './methods/saveDepartment'; diff --git a/app/livechat/server/lib/Livechat.js b/app/livechat/server/lib/Livechat.js index 1717d2166969f..d61fa4e55b633 100644 --- a/app/livechat/server/lib/Livechat.js +++ b/app/livechat/server/lib/Livechat.js @@ -29,7 +29,7 @@ import { LivechatOfficeHour, } from '../../../models'; import { Logger } from '../../../logger'; -import { addUserRoles, removeUserFromRoles } from '../../../authorization'; +import { addUserRoles, hasRole, removeUserFromRoles } from '../../../authorization'; import * as Mailer from '../../../mailer'; import { LivechatInquiry } from '../../lib/LivechatInquiry'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; @@ -650,9 +650,13 @@ export const Livechat = { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'livechat:removeAgent' }); } - if (removeUserFromRoles(user._id, 'livechat-agent')) { - Users.setOperator(user._id, false); - this.setUserStatusLivechat(user._id, 'not-available'); + const { _id } = user; + + if (removeUserFromRoles(_id, 'livechat-agent')) { + Users.setOperator(_id, false); + Users.removeLivechatData(_id); + this.setUserStatusLivechat(_id, 'not-available'); + LivechatDepartmentAgents.removeByAgentId(_id); return true; } @@ -761,6 +765,23 @@ export const Livechat = { return LivechatDepartment.createOrUpdateDepartment(_id, departmentData, departmentAgents); }, + saveAgentInfo(_id, agentData, agentDepartments) { + check(_id, Match.Maybe(String)); + check(agentData, Object); + check(agentDepartments, [String]); + + + const user = Users.findOneById(_id); + if (!user || !hasRole(_id, 'livechat-agent')) { + throw new Meteor.Error('error-user-is-not-agent', 'User is not a livechat agent', { method: 'livechat:saveAgentInfo' }); + } + + Users.setLivechatData(_id, agentData); + LivechatDepartment.saveDepartmentsByAgent(user, agentDepartments); + + return true; + }, + removeDepartment(_id) { check(_id, String); diff --git a/app/livechat/server/methods/saveAgentInfo.js b/app/livechat/server/methods/saveAgentInfo.js new file mode 100644 index 0000000000000..9b12a428219f7 --- /dev/null +++ b/app/livechat/server/methods/saveAgentInfo.js @@ -0,0 +1,20 @@ +import { Meteor } from 'meteor/meteor'; + +import { hasPermission, hasRole } from '../../../authorization'; +import { Livechat } from '../lib/Livechat'; +import { Users } from '../../../models'; + +Meteor.methods({ + 'livechat:saveAgentInfo'(_id, agentData, agentDepartments) { + if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'manage-livechat-agents')) { + throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:saveAgentInfo' }); + } + + const user = Users.findOneById(_id); + if (!user || !hasRole(_id, 'livechat-agent')) { + throw new Meteor.Error('error-user-is-not-agent', 'User is not a livechat agent', { method: 'livechat:saveAgentInfo' }); + } + + return Livechat.saveAgentInfo(_id, agentData, agentDepartments); + }, +}); diff --git a/app/models/server/models/LivechatDepartment.js b/app/models/server/models/LivechatDepartment.js index 4b36c464cdcda..a57238234c40c 100644 --- a/app/models/server/models/LivechatDepartment.js +++ b/app/models/server/models/LivechatDepartment.js @@ -68,6 +68,36 @@ export class LivechatDepartment extends Base { return _.extend(record, { _id }); } + saveDepartmentsByAgent(agent, departments = []) { + const { _id: agentId, username } = agent; + const savedDepartments = LivechatDepartmentAgents.findByAgentId(agentId).fetch().map((d) => d.departmentId); + + const incNumAgents = (_id, numAgents) => this.update(_id, { $inc: { numAgents } }); + // remove other departments + _.difference(savedDepartments, departments).forEach((departmentId) => { + LivechatDepartmentAgents.removeByDepartmentIdAndAgentId(departmentId, agentId); + incNumAgents(departmentId, -1); + }); + + departments.forEach((departmentId) => { + const saveResult = LivechatDepartmentAgents.saveAgent({ + agentId, + departmentId, + username, + count: 0, + order: 0, + }); + + if (saveResult.insertedId) { + incNumAgents(departmentId, 1); + } + }); + } + + updateById(_id, update) { + return this.update({ _id }, update); + } + // REMOVE removeById(_id) { const query = { _id }; diff --git a/app/models/server/models/LivechatDepartmentAgents.js b/app/models/server/models/LivechatDepartmentAgents.js index 2184478621c00..6ca6a2345428d 100644 --- a/app/models/server/models/LivechatDepartmentAgents.js +++ b/app/models/server/models/LivechatDepartmentAgents.js @@ -36,6 +36,10 @@ export class LivechatDepartmentAgents extends Base { }); } + removeByAgentId(agentId) { + this.remove({ agentId }); + } + removeByDepartmentIdAndAgentId(departmentId, agentId) { this.remove({ departmentId, agentId }); } diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index fcce77ca255ed..060494491021d 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -237,6 +237,20 @@ export class Users extends Base { return this.update(query, update); } + setLivechatData(userId, data = {}) { + const query = { + _id: userId, + }; + + const update = { + $set: { + livechat: data, + }, + }; + + return this.update(query, update); + } + closeOffice() { this.findAgents().forEach((agent) => this.setLivechatStatus(agent._id, 'not-available')); } @@ -257,6 +271,7 @@ export class Users extends Base { phone: 1, customFields: 1, status: 1, + livechat: 1, }, }; @@ -1151,6 +1166,20 @@ export class Users extends Base { return this.remove(_id); } + removeLivechatData(userId) { + const query = { + _id: userId, + }; + + const update = { + $unset: { + livechat: true, + }, + }; + + return this.update(query, update); + } + /* Find users to send a message by email if: - he is not online diff --git a/app/ui-utils/client/lib/AccountBox.js b/app/ui-utils/client/lib/AccountBox.js index 41079b9a59682..7216ceec74ccc 100644 --- a/app/ui-utils/client/lib/AccountBox.js +++ b/app/ui-utils/client/lib/AccountBox.js @@ -62,8 +62,9 @@ export const AccountBox = (function() { if (router == null) { router = FlowRouter; } + const container = newRoute.customContainer ? 'pageCustomContainer' : 'pageContainer'; const routeConfig = { - center: 'pageContainer', + center: container, pageTemplate: newRoute.pageTemplate, }; if (newRoute.i18nPageTitle != null) { diff --git a/app/ui/client/index.js b/app/ui/client/index.js index 39f35a953ee3d..485346a1bb4e0 100644 --- a/app/ui/client/index.js +++ b/app/ui/client/index.js @@ -19,6 +19,7 @@ import './views/app/fullModal.html'; import './views/app/home.html'; import './views/app/notAuthorized.html'; import './views/app/pageContainer.html'; +import './views/app/pageCustomContainer.html'; import './views/app/pageSettingsContainer.html'; import './views/app/room.html'; import './views/app/roomSearch.html'; diff --git a/app/ui/client/views/app/pageCustomContainer.html b/app/ui/client/views/app/pageCustomContainer.html new file mode 100644 index 0000000000000..d4f59f354e065 --- /dev/null +++ b/app/ui/client/views/app/pageCustomContainer.html @@ -0,0 +1,3 @@ + diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 063067b8cc993..8de901e4e6fcf 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -246,6 +246,7 @@ "Advocacy": "Advocacy", "After_OAuth2_authentication_users_will_be_redirected_to_this_URL": "After OAuth2 authentication, users will be redirected to an URL on this list. You can add one URL per line.", "Agent": "Agent", + "Agent_Info": "Agent Info", "Agents": "Agents", "Agent_added": "Agent added", "Agent_removed": "Agent removed", @@ -1340,6 +1341,7 @@ "error-too-many-requests": "Error, too many requests. Please slow down. You must wait __seconds__ seconds before trying again.", "error-user-has-no-roles": "User has no roles", "error-user-is-not-activated": "User is not activated", + "error-user-is-not-agent": "User is not a Livechat Agent", "error-user-is-offline": "User if offline", "error-user-limit-exceeded": "The number of users you are trying to invite to #channel_name exceeds the limit set by the administrator", "error-user-not-in-room": "User is not in this room", @@ -2774,10 +2776,11 @@ "Select_a_user": "Select a user", "Select_an_avatar": "Select an avatar", "Select_an_option": "Select an option", + "Select_department": "Select a department", "Select_file": "Select file", "Select_role": "Select a Role", "Select_service_to_login": "Select a service to login to load your picture or upload one directly from your computer", - "Select_tag": "Select uma tag", + "Select_tag": "Select a tag", "Select_user": "Select user", "Select_users": "Select users", "Selected_agents": "Selected agents", @@ -3174,6 +3177,7 @@ "unarchive-room": "Unarchive Room", "unarchive-room_description": "Permission to unarchive channels", "Unblock_User": "Unblock User", + "Undefined": "Undefined", "Unfavorite": "Unfavorite", "Unfollow_message": "Unfollow message", "Unignore": "Unignore", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index 674ad2f94343f..b09f1f6ebd585 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -2595,6 +2595,7 @@ "Select_a_user": "Selecione um usuário", "Select_an_avatar": "Selecione um avatar", "Select_an_option": "Selecione uma opção", + "Select_department": "Selecione um departmento", "Select_file": "Selecione um arquivo", "Select_role": "Selecione um papel", "Select_service_to_login": "Selecione um serviço para iniciar sessão e carregar sua imagem ou faça upload de um arquivo de seu computador", @@ -2968,6 +2969,7 @@ "unarchive-room": "Desarquivar Sala", "unarchive-room_description": "Permissão para desarchivar canais", "Unblock_User": "Desbloquear Usuário", + "Undefined": "Não definido", "Unfavorite": "Remover dos Favoritos", "Unfollow_message": "Deixar de seguir mensagem", "Unignore": "Unignore", diff --git a/packages/rocketchat-i18n/i18n/pt.i18n.json b/packages/rocketchat-i18n/i18n/pt.i18n.json index 364287a279014..02b8dda305da7 100644 --- a/packages/rocketchat-i18n/i18n/pt.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt.i18n.json @@ -236,6 +236,7 @@ "Advocacy": "Apoio dado", "After_OAuth2_authentication_users_will_be_redirected_to_this_URL": "Após a autenticação OAuth2, os utilizadores serão redireccionados para esta URL", "Agent": "Agente", + "Agent_Info": "Informações do Agente", "Agent_added": "Agente adicionado", "Agent_removed": "Agente removido", "Alerts": "Alertas", @@ -1262,6 +1263,7 @@ "error-user-has-no-roles": "O utilizador não tem funções", "error-user-is-not-activated": "O utilizador não está activo", "error-user-limit-exceeded": "O número de utilizadores que está a tentar convidar para #channel_name excede o limite definido pelo administrador", + "error-user-is-not-agent": "O usuário não é um agente de livechat", "error-user-not-in-room": "O utilizador não está nesta sala", "error-logged-user-not-in-room": "Não está na sala `%s`", "error-user-registration-disabled": "O registo de utilizadores está desabilitado", @@ -3214,4 +3216,4 @@ "Your_question": "A sua pergunta", "Your_server_link": "O link do seu servidor", "Your_workspace_is_ready": "O seu espaço de trabalho está pronto a usar 🎉" -} \ No newline at end of file +}