Skip to content

Commit

Permalink
[NEW] Add default chat closing tags in Omnichannel departments (#16859)
Browse files Browse the repository at this point in the history
  • Loading branch information
renatobecker authored Mar 19, 2020
1 parent a102c6f commit 0340d56
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 18 deletions.
44 changes: 44 additions & 0 deletions app/livechat/client/views/app/livechatDepartmentForm.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,50 @@
<label><input type="radio" name="requestTagBeforeClosingChat" value="0" checked="{{$eq requestTagBeforeClosingChat false}}" /> {{_ "No"}}</label>
</div>
</div>
<label>{{_ "Conversation_closing_tags"}}</label>
<div class="input-line form-inline">
<div class="form-group">
{{#if hasAvailableTags}}
<div class="rc-input__wrapper">
<select id="tagSelect" class="rc-input rc-input__element rc-input--small rc-form-item-inline">
<option value="placeholder" disabled selected>{{_ "Select_tag"}}</option>
{{#each availableDepartmentTags}}
<option value="{{_id}}">{{this}}</option>
{{/each}}
</select>
</div>
{{else}}
<div class="rc-input" id="add-tag-input">
<label class="rc-input__label">
<div class="rc-input__wrapper">
<div class="rc-input__icon">
{{> icon icon='edit' }}
</div>
<input id="tagInput" class="rc-input__element" type="text" name="tags" autocomplete="off" placeholder="{{_"Enter_a_tag"}}">
</div>
</label>
</div>
{{/if}}
</div>
<div class="form-group">
<button id="addTag" name="addTag" class="rc-button rc-button--primary add-tag">{{_ "Add"}}</button>
</div>
<div>
<small class="secondary-font-color">{{{_ "Conversation_closing_tags_description"}}}</small>
</div>
</div>
{{#if hasChatClosingTags}}
<div class="input-line">
<ul id="tags" class="chip-container department-fallback-tags">
{{#each chatClosingTags}}
<li class="remove-tag" title="{{this}}">
<i class="icon icon-cancel-circled"></i>
{{this}}
</li>
{{/each}}
</ul>
</div>
{{/if}}
{{#if customFieldsTemplate}}
{{> Template.dynamic template=customFieldsTemplate data=data }}
{{/if}}
Expand Down
60 changes: 58 additions & 2 deletions app/livechat/client/views/app/livechatDepartmentForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ Template.livechatDepartmentForm.helpers({
.some((button) => button.groups
.some((group) => group.startsWith('livechat-department')));
},
chatClosingTags() {
return Template.instance().chatClosingTags.get();
},
availableDepartmentTags() {
return Template.instance().availableDepartmentTags.get();
},
hasAvailableTags() {
return [...Template.instance().availableTags.get()].length > 0;
},
hasChatClosingTags() {
return [...Template.instance().chatClosingTags.get()].length > 0;
},
});

Template.livechatDepartmentForm.events({
Expand All @@ -98,7 +110,7 @@ Template.livechatDepartmentForm.events({
const email = instance.$('input[name=email]').val();
const showOnOfflineForm = instance.$('input[name=showOnOfflineForm]:checked').val();
const requestTagBeforeClosingChat = instance.$('input[name=requestTagBeforeClosingChat]:checked').val();

const chatClosingTags = instance.chatClosingTags.get();
if (enabled !== '1' && enabled !== '0') {
return toastr.error(t('Please_select_enabled_yes_or_no'));
}
Expand All @@ -119,6 +131,7 @@ Template.livechatDepartmentForm.events({
showOnOfflineForm: showOnOfflineForm === '1',
requestTagBeforeClosingChat: requestTagBeforeClosingChat === '1',
email: email.trim(),
chatClosingTags,
};
}

Expand Down Expand Up @@ -190,6 +203,33 @@ Template.livechatDepartmentForm.events({

instance.departmentAgents.set(instance.departmentAgents.get().filter((agent) => agent.agentId !== this.agentId));
},

'click #addTag'(e, instance) {
e.stopPropagation();
e.preventDefault();

const isSelect = [...instance.availableTags.get()].length > 0;
const elId = isSelect ? '#tagSelect' : '#tagInput';
const elDefault = isSelect ? 'placeholder' : '';

const tag = $(elId).val();
const chatClosingTags = [...instance.chatClosingTags.get()];
if (tag === '' || chatClosingTags.indexOf(tag) > -1) {
return;
}

chatClosingTags.push(tag);
instance.chatClosingTags.set(chatClosingTags);
$(elId).val(elDefault);
},

'click .remove-tag'(e, instance) {
e.stopPropagation();
e.preventDefault();

const chatClosingTags = [...instance.chatClosingTags.get()].filter((el) => el !== this.valueOf());
instance.chatClosingTags.set(chatClosingTags);
},
});

Template.livechatDepartmentForm.onCreated(async function() {
Expand All @@ -199,21 +239,37 @@ Template.livechatDepartmentForm.onCreated(async function() {
this.tabBar = new RocketChatTabBar();
this.tabBar.showGroup(FlowRouter.current().route.name);
this.tabBarData = new ReactiveVar();
this.chatClosingTags = new ReactiveVar([]);
this.availableTags = new ReactiveVar([]);
this.availableDepartmentTags = new ReactiveVar([]);

this.onSelectAgents = ({ item: agent }) => {
this.selectedAgents.set([agent]);
};

this.onClickTagAgent = ({ username }) => {
this.onClickTagAgents = ({ username }) => {
this.selectedAgents.set(this.selectedAgents.get().filter((user) => user.username !== username));
};

this.loadAvailableTags = (departmentId) => {
Meteor.call('livechat:getTagsList', (err, tagsList) => {
this.availableTags.set(tagsList || []);
const tags = this.availableTags.get();
const availableTags = tags
.filter(({ departments }) => departments.length === 0 || departments.indexOf(departmentId) > -1)
.map(({ name }) => name);
this.availableDepartmentTags.set(availableTags);
});
};

this.autorun(async () => {
const id = FlowRouter.getParam('_id');
if (id) {
const { department, agents } = await APIClient.v1.get(`livechat/department/${ FlowRouter.getParam('_id') }`);
this.department.set(department);
this.departmentAgents.set(agents);
this.chatClosingTags.set((department && department.chatClosingTags) || []);
this.loadAvailableTags(id);
}
});
});
2 changes: 1 addition & 1 deletion app/livechat/client/views/app/tabbar/visitorInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ Template.visitorInfo.events({
'click .close-livechat'(event) {
event.preventDefault();

const closeRoom = (comment) => Meteor.call('livechat:closeRoom', this.rid, comment, function(error/* , result*/) {
const closeRoom = (comment) => Meteor.call('livechat:closeRoom', this.rid, comment, { clientAction: true }, function(error/* , result*/) {
if (error) {
return handleError(error);
}
Expand Down
30 changes: 22 additions & 8 deletions app/livechat/server/hooks/beforeCloseRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,34 @@ import { Meteor } from 'meteor/meteor';
import { callbacks } from '../../../callbacks';
import { LivechatDepartment } from '../../../models';

callbacks.add('livechat.beforeCloseRoom', (room) => {
const { departmentId } = room;
const concatUnique = (...arrays) => [...new Set([].concat(...arrays.filter(Array.isArray)))];

callbacks.add('livechat.beforeCloseRoom', ({ room, options }) => {
const { departmentId, tags: roomTags } = room;
if (!departmentId) {
return room;
return;
}

const department = LivechatDepartment.findOneById(departmentId);
if (!department || !department.requestTagBeforeClosingChat) {
return room;
if (!department) {
return;
}

const { requestTagBeforeClosingChat, chatClosingTags } = department;
const extraData = {
tags: concatUnique(roomTags, chatClosingTags),
};

if (!requestTagBeforeClosingChat) {
return extraData;
}

if (room.tags && room.tags.length > 0) {
return room;
const { clientAction } = options;
const checkRoomTags = !clientAction || (roomTags && roomTags.length > 0);
const checkDepartmentTags = chatClosingTags && chatClosingTags.length > 0;
if (!checkRoomTags || !checkDepartmentTags) {
throw new Meteor.Error('error-tags-must-be-assigned-before-closing-chat', 'Tag(s) must be assigned before closing the chat', { method: 'livechat.beforeCloseRoom' });
}

throw new Meteor.Error('error-tags-must-be-assigned-before-closing-chat', 'Tag(s) must be assigned before closing the chat', { method: 'livechat.beforeCloseRoom' });
return extraData;
}, callbacks.priority.HIGH, 'livechat-before-close-Room');
12 changes: 10 additions & 2 deletions app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,19 @@ export const Livechat = {
return ret;
},

closeRoom({ user, visitor, room, comment }) {
closeRoom({ user, visitor, room, comment, options = {} }) {
if (!room || room.t !== 'l' || !room.open) {
return false;
}

callbacks.run('livechat.beforeCloseRoom', room);
const extraData = callbacks.run('livechat.beforeCloseRoom', { room, options });

const now = new Date();

const closeData = {
closedAt: now,
chatDuration: (now.getTime() - room.ts) / 1000,
...extraData,
};

if (user) {
Expand Down Expand Up @@ -807,6 +808,8 @@ export const Livechat = {
showOnRegistration: Boolean,
email: String,
showOnOfflineForm: Boolean,
requestTagBeforeClosingChat: Match.Optional(Boolean),
chatClosingTags: Match.Optional([String]),
};

// The Livechat Form department support addition/custom fields, so those fields need to be added before validating
Expand All @@ -825,6 +828,11 @@ export const Livechat = {
}),
]));

const { requestTagBeforeClosingChat, chatClosingTags } = departmentData;
if (requestTagBeforeClosingChat && (!chatClosingTags || chatClosingTags.length === 0)) {
throw new Meteor.Error('error-validating-department-chat-closing-tags', 'At least one closing tag is required when the department requires tag(s) on closing conversations.', { method: 'livechat:saveDepartment' });
}

if (_id) {
const department = LivechatDepartment.findOneById(_id);
if (!department) {
Expand Down
3 changes: 2 additions & 1 deletion app/livechat/server/methods/closeRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Subscriptions, LivechatRooms } from '../../../models';
import { Livechat } from '../lib/Livechat';

Meteor.methods({
'livechat:closeRoom'(roomId, comment) {
'livechat:closeRoom'(roomId, comment, options = {}) {
const userId = Meteor.userId();
if (!userId || !hasPermission(userId, 'close-livechat-room')) {
throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'livechat:closeRoom' });
Expand All @@ -31,6 +31,7 @@ Meteor.methods({
user,
room: LivechatRooms.findOneById(roomId),
comment,
options,
});
},
});
11 changes: 7 additions & 4 deletions app/models/server/models/LivechatRooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,16 +421,19 @@ export class LivechatRooms extends Base {


closeByRoomId(roomId, closeInfo) {
const { closer, closedBy, closedAt, chatDuration, ...extraData } = closeInfo;

return this.update({
_id: roomId,
t: 'l',
}, {
$set: {
closer: closeInfo.closer,
closedBy: closeInfo.closedBy,
closedAt: closeInfo.closedAt,
'metrics.chatDuration': closeInfo.chatDuration,
closer,
closedBy,
closedAt,
'metrics.chatDuration': chatDuration,
'v.status': 'offline',
...extraData,
},
$unset: {
open: 1,
Expand Down
3 changes: 3 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,8 @@
"Conversation": "Conversation",
"Conversations": "Conversations",
"Conversation_closed": "Conversation closed: __comment__.",
"Conversation_closing_tags": "Conversation closing tags",
"Conversation_closing_tags_description": "Closing tags will be automatically assigned to conversations at closing.",
"Conversation_finished": "Conversation Finished",
"Conversation_finished_message": "Conversation Finished Message",
"Conversation_finished_text": "Conversation Finished Text",
Expand Down Expand Up @@ -1402,6 +1404,7 @@
"error-logged-user-not-in-room": "You are not in the room `%s`",
"error-user-registration-disabled": "User registration is disabled",
"error-user-registration-secret": "User registration is only allowed via Secret URL",
"error-validating-department-chat-closing-tags": "At least one closing tag is required when the department requires tag(s) on closing conversations.",
"error-you-are-last-owner": "You are the last owner. Please set new owner before leaving the room.",
"error-starring-message": "Message could not be stared",
"Error_404": "Error:404",
Expand Down
3 changes: 3 additions & 0 deletions packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,8 @@
"Conversation": "Conversa",
"Conversations": "Conversas",
"Conversation_closed": "Chat encerrado: __comment__.",
"Conversation_closing_tags": "Tags de encerramento de conversa",
"Conversation_closing_tags_description": "As tags de encerramento serão automaticamente atribuídas as conversas no seu encerramento.",
"Conversation_finished": "Conversa concluída",
"Conversation_finished_message": "Mensagem de conversa concluída",
"Conversation_finished_text": "Texto de conversa concluída",
Expand Down Expand Up @@ -1303,6 +1305,7 @@
"error-logged-user-not-in-room": "Você não está na sala `%s`",
"error-user-registration-disabled": "O registro do usuário está desativado",
"error-user-registration-secret": "O registro de usuário é permitido somente via URL secreta",
"error-validating-department-chat-closing-tags": "Pelo menos uma tag de encerramento é necessária quando o departamento exige tags no encerramento de conversas.",
"error-you-are-last-owner": "Você é o último proprietário da sala. Por favor defina um novo proprietário antes de sair.",
"Error_404": "Erro 404",
"Error_changing_password": "Erro ao alterar senha",
Expand Down

0 comments on commit 0340d56

Please sign in to comment.