Skip to content

Commit

Permalink
[IMPROVE] Replace the Department select component by an Autocomplete …
Browse files Browse the repository at this point in the history
…input in Omnichannel UI (#16669)
  • Loading branch information
renatobecker authored Mar 19, 2020
1 parent 0340d56 commit f1d8563
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 50 deletions.
31 changes: 19 additions & 12 deletions app/livechat/client/views/app/livechatCurrentChats.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,25 @@
</label>
</div>
<div class="form-group">
<label class="rc-input__label">
<div class="rc-input__title">{{_ "Department"}}</div>
<div class="rc-select">
<select class="rc-select__element" id="department" name="department">
<option class="rc-select__option" value="">{{_ "Select_a_department"}}</option>
{{#each departments}}
<option class="rc-select__option" value="{{_id}}">{{name}}</option>
{{/each}}
</select>
{{> icon block="rc-select__arrow" icon="arrow-down" }}
</div>
</label>
{{> livechatAutocompleteUser
onClickTag=onClickTagDepartment
list=selectedDepartments
onSelect=onSelectDepartments
collection='CachedDepartmentList'
endpoint='livechat/department.autocomplete'
field='name'
sort='name'
label="Department"
placeholder="Enter_a_department_name"
name="department"
icon="queue"
noMatchTemplate="userSearchEmpty"
templateItem="popupList_item_channel"
template="roomSearch"
noMatchTemplate="roomSearchEmpty"
modifier=departmentModifier
showLabel=true
}}
</div>
<div class="form-group input-daterange">
<label class="rc-input__label">
Expand Down
46 changes: 36 additions & 10 deletions app/livechat/client/views/app/livechatCurrentChats.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,24 @@ Template.livechatCurrentChats.helpers({
tagFilters() {
return Template.instance().tagFilters.get();
},
departments() {
return Template.instance().departments.get();
},
tagId() {
return this;
},
departmentModifier() {
return (filter, text = '') => {
const f = filter.get();
return `${ f.length === 0 ? text : text.replace(new RegExp(filter.get(), 'i'), (part) => `<strong>${ part }</strong>`) }`;
};
},
onClickTagDepartment() {
return Template.instance().onClickTagDepartment;
},
selectedDepartments() {
return Template.instance().selectedDepartments.get();
},
onSelectDepartments() {
return Template.instance().onSelectDepartments;
},
});

Template.livechatCurrentChats.events({
Expand Down Expand Up @@ -302,6 +314,11 @@ Template.livechatCurrentChats.events({
filter.agents = [agents[0]];
}

const departments = instance.selectedDepartments.get();
if (departments && departments.length > 0) {
filter.department = [departments[0]];
}

instance.filter.set(filter);
instance.offset.set(0);
storeFilters(filter);
Expand Down Expand Up @@ -353,7 +370,16 @@ Template.livechatCurrentChats.onCreated(async function() {
this.customFilters = new ReactiveVar([]);
this.customFields = new ReactiveVar([]);
this.tagFilters = new ReactiveVar([]);
this.departments = new ReactiveVar([]);
this.selectedDepartments = new ReactiveVar([]);

this.onSelectDepartments = ({ item: department }) => {
department.text = department.name;
this.selectedDepartments.set([department]);
};

this.onClickTagDepartment = () => {
this.selectedDepartments.set([]);
};

const mountArrayQueryParameters = (label, items, index) => items.reduce((acc, item) => {
const isTheLastElement = index === items.length - 1;
Expand All @@ -368,8 +394,8 @@ Template.livechatCurrentChats.onCreated(async function() {
if (status) {
url += `&open=${ status === 'opened' }`;
}
if (department) {
url += `&departmentId=${ department }`;
if (department && Array.isArray(department) && department.length) {
url += `&departmentId=${ department[0]._id }`;
}
if (from) {
dateRange.start = `${ moment(new Date(from)).utc().format('YYYY-MM-DDTHH:mm:ss') }Z`;
Expand Down Expand Up @@ -407,6 +433,8 @@ Template.livechatCurrentChats.onCreated(async function() {
switch (key) {
case 'agents':
return this.selectedAgents.set(value);
case 'department':
return this.selectedDepartments.set(value);
case 'from':
case 'to':
return $(`#${ key }`).datepicker('setDate', new Date(value));
Expand All @@ -420,6 +448,7 @@ Template.livechatCurrentChats.onCreated(async function() {
removeStoredFilters();
$('#form-filters').get(0).reset();
this.selectedAgents.set([]);
this.selectedDepartments.set([]);
this.filter.set({});
};

Expand Down Expand Up @@ -449,16 +478,13 @@ Template.livechatCurrentChats.onCreated(async function() {
this.loadRooms(filter, offset);
});

const { departments } = await APIClient.v1.get('livechat/department?sort={"name": 1}');
this.departments.set(departments);

Meteor.call('livechat:getCustomFields', (err, customFields) => {
if (customFields) {
this.customFields.set(customFields);
}
});

setTimeout(this.loadDefaultFilters, 500);
this.loadDefaultFilters();
});

Template.livechatCurrentChats.onRendered(function() {
Expand Down
32 changes: 22 additions & 10 deletions app/livechat/client/views/app/tabbar/visitorForward.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,34 @@ <h3>{{_ "Forward_chat"}}</h3>
<form>
{{#if hasDepartments}}
<div class="input-line">
<label for="forwardDepartment">{{_ "Forward_to_department"}}</label>
<div class="rc-select">
<select class="rc-select__element" name="forwardDepartment" id="forwardDepartment">
<option class="rc-select__option" value="">{{_ "Select_a_department"}}</option>
{{#each departments}}
<option class="rc-select__option" value="{{_id}}">{{name}}</option>
{{/each}}
</select>
{{> icon block="rc-select__arrow" icon="arrow-down" }}
<label for="name">{{_ "Forward_to_department"}}</label>
<div class="form-group">
{{> livechatAutocompleteUser
onClickTag=onClickTagDepartment
list=selectedDepartments
onSelect=onSelectDepartments
collection='CachedDepartmentList'
endpoint='livechat/department.autocomplete'
field='name'
sort='name'
icon="queue"
label="Enter_a_department_name"
placeholder="Enter_a_department_name"
name="department"
noMatchTemplate="userSearchEmpty"
templateItem="popupList_item_channel"
template="roomSearch"
noMatchTemplate="roomSearchEmpty"
modifier=departmentModifier
conditions=departmentConditions
}}
</div>
</div>

<div class="form-divisor">
<span>{{_ "or"}}</span>
</div>
{{/if}}

<div class="input-line">
<label for="agent">{{_ "Forward_to_user"}}</label>
<div class="form-group">
Expand Down
54 changes: 37 additions & 17 deletions app/livechat/client/views/app/tabbar/visitorForward.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ Template.visitorForward.helpers({
hasDepartments() {
return Template.instance().departments.get().filter((department) => department.enabled === true).length > 0;
},
departments() {
return Template.instance().departments.get().filter((department) => department.enabled === true);
},
agentName() {
return this.name || this.username;
},
Expand All @@ -43,16 +40,46 @@ Template.visitorForward.helpers({
onClickTagAgent() {
return Template.instance().onClickTagAgent;
},
departmentModifier() {
return (filter, text = '') => {
const f = filter.get();
return `${ f.length === 0 ? text : text.replace(new RegExp(filter.get(), 'i'), (part) => `<strong>${ part }</strong>`) }`;
};
},
onClickTagDepartment() {
return Template.instance().onClickTagDepartment;
},
selectedDepartments() {
return Template.instance().selectedDepartments.get();
},
onSelectDepartments() {
return Template.instance().onSelectDepartments;
},
departmentConditions() {
return { enabled: true, numAgents: { $gt: 0 } };
},
});

Template.visitorForward.onCreated(async function() {
this.visitor = new ReactiveVar();
this.room = new ReactiveVar();
this.departments = new ReactiveVar([]);
this.selectedAgents = new ReactiveVar([]);
this.selectedDepartments = new ReactiveVar([]);

this.onSelectDepartments = ({ item: department }) => {
department.text = department.name;
this.selectedDepartments.set([department]);
this.selectedAgents.set([]);
};

this.onClickTagDepartment = () => {
this.selectedDepartments.set([]);
};

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

this.onClickTagAgent = ({ username }) => {
Expand Down Expand Up @@ -83,8 +110,13 @@ Template.visitorForward.events({
const [user] = instance.selectedAgents.get();
if (user) {
transferData.userId = user._id;
} else if (instance.find('#forwardDepartment').value) {
transferData.departmentId = instance.find('#forwardDepartment').value;
} else if (instance.selectedDepartments.get()) {
const [department] = instance.selectedDepartments.get();
transferData.departmentId = department && department._id;
}

if (!transferData.userId && !transferData.departmentId) {
return;
}

Meteor.call('livechat:transfer', transferData, (error, result) => {
Expand All @@ -100,18 +132,6 @@ Template.visitorForward.events({
});
},

'change #forwardDepartment, blur #forwardDepartment'(event, instance) {
if (event.currentTarget.value) {
instance.find('#forwardUser').value = '';
}
},

'change #forwardUser, blur #forwardUser'(event, instance) {
if (event.currentTarget.value && instance.find('#forwardDepartment')) {
instance.find('#forwardDepartment').value = '';
}
},

'click .cancel'(event) {
event.preventDefault();

Expand Down
16 changes: 15 additions & 1 deletion app/livechat/imports/server/rest/departments.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { API } from '../../../../api';
import { hasPermission } from '../../../../authorization';
import { LivechatDepartment, LivechatDepartmentAgents } from '../../../../models';
import { Livechat } from '../../../server/lib/Livechat';
import { findDepartments, findDepartmentById } from '../../../server/api/lib/departments';
import { findDepartments, findDepartmentById, findDepartmentsToAutocomplete } from '../../../server/api/lib/departments';

API.v1.addRoute('livechat/department', { authRequired: true }, {
get() {
Expand Down Expand Up @@ -133,3 +133,17 @@ API.v1.addRoute('livechat/department/:_id', { authRequired: true }, {
}
},
});

API.v1.addRoute('livechat/department.autocomplete', { authRequired: true }, {
get() {
const { selector } = this.queryParams;
if (!selector) {
return API.v1.failure('The \'selector\' param is required');
}

return API.v1.success(Promise.await(findDepartmentsToAutocomplete({
uid: this.userId,
selector: JSON.parse(selector),
})));
},
});
23 changes: 23 additions & 0 deletions app/livechat/server/api/lib/departments.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,26 @@ export async function findDepartmentById({ userId, departmentId, includeAgents =

return result;
}

export async function findDepartmentsToAutocomplete({ uid, selector }) {
if (!await hasPermissionAsync(uid, 'view-livechat-departments') && !await hasPermissionAsync(uid, 'view-l-room')) {
return { items: [] };
}
const { exceptions = [], conditions = {} } = selector;

const options = {
fields: {
_id: 1,
name: 1,
},
limit: 10,
sort: {
name: 1,
},
};

const items = await LivechatDepartment.findByNameRegexWithExceptionsAndConditions(selector.term, exceptions, conditions, options).toArray();
return {
items,
};
}
20 changes: 20 additions & 0 deletions app/models/server/raw/LivechatDepartment.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,28 @@
import s from 'underscore.string';

import { BaseRaw } from './BaseRaw';

export class LivechatDepartmentRaw extends BaseRaw {
findInIds(departmentsIds, options) {
const query = { _id: { $in: departmentsIds } };
return this.find(query, options);
}

findByNameRegexWithExceptionsAndConditions(searchTerm, exceptions = [], conditions = {}, options = {}) {
if (!Array.isArray(exceptions)) {
exceptions = [exceptions];
}

const nameRegex = new RegExp(`^${ s.escapeRegExp(searchTerm).trim() }`, 'i');

const query = {
name: nameRegex,
_id: {
$nin: exceptions,
},
...conditions,
};

return this.find(query, options);
}
}
1 change: 1 addition & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,7 @@
"End_OTR": "End OTR",
"Enter_a_name": "Enter a name",
"Enter_a_regex": "Enter a regex",
"Enter_a_department_name": "Enter a department name",
"Enter_a_room_name": "Enter a room name",
"Enter_a_username": "Enter a username",
"Enter_a_tag": "Enter a tag",
Expand Down
1 change: 1 addition & 0 deletions packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,7 @@
"End_OTR": "Finalizar OTR",
"Enter_a_name": "Insira o nome aqui",
"Enter_a_regex": "Introduza um regex",
"Enter_a_department_name": "Digite um nome de departmento",
"Enter_a_room_name": "Digite um nome de sala",
"Enter_a_username": "Nome de usuário",
"Enter_a_tag": "Insira uma tag",
Expand Down

0 comments on commit f1d8563

Please sign in to comment.