New status for livechat agents (#2821)

* New field to track livechat agent availability

* Fix survey UI issues

* Fix for Codacy
pull/2817/head^2
Diego Sampaio 10 years ago committed by Gabriel Engel
parent 3fa06449fc
commit 3bd2d95f42
  1. 2
      packages/rocketchat-lib/i18n/en.i18n.json
  2. 12
      packages/rocketchat-livechat/app/client/stylesheets/main.less
  3. 2
      packages/rocketchat-livechat/app/client/views/survey.html
  4. 13
      packages/rocketchat-livechat/client/methods/changeLivechatStatus.js
  5. 48
      packages/rocketchat-livechat/client/stylesheets/livechat.less
  6. 26
      packages/rocketchat-livechat/client/views/sideNav/livechat.html
  7. 26
      packages/rocketchat-livechat/client/views/sideNav/livechat.js
  8. 3
      packages/rocketchat-livechat/package.js
  9. 4
      packages/rocketchat-livechat/server/methods/addAgent.js
  10. 13
      packages/rocketchat-livechat/server/methods/changeLivechatStatus.js
  11. 4
      packages/rocketchat-livechat/server/methods/removeAgent.js
  12. 24
      packages/rocketchat-livechat/server/models/Users.js
  13. 1
      server/publications/userData.coffee

@ -150,6 +150,7 @@
"AutoLinker_Phone": "AutoLinker Phone",
"AutoLinker_Phone_Description": "Automatically linked for Phone numbers. e.g. `(123)456-7890`",
"Auto_Load_Images" : "Auto Load Images",
"Available" : "Available",
"Available_agents" : "Available agents",
"Avatar" : "Avatar",
"Avatar_changed_successfully" : "Avatar changed successfully",
@ -606,6 +607,7 @@
"No_user_with_username_%s_was_found" : "No user with username <strong>\"%s\"</strong> was found!",
"Not_allowed" : "Not allowed",
"Not_authorized" : "Not authorized",
"Not_Available" : "Not Available",
"Not_found_or_not_allowed" : "Not Found or Not Allowed",
"Nothing" : "Nothing",
"Nothing_found" : "Nothing found",

@ -447,10 +447,10 @@ input:focus {
z-index: 9999;
background: white;
position: fixed;
height: 60vh;
width: 60vw;
top: 20vh;
left: 20vw;
height: 80%;
width: 80%;
top: 15%;
left: 10%;
border-radius: 6px;
display: flex;
flex-direction: column;
@ -460,6 +460,10 @@ input:focus {
padding: 0 15px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
line-height: 40px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.content {

@ -2,7 +2,7 @@
<div id="survey">
<div class="overlay"></div>
<div class="wrapper">
<header>
<header title="{{_ 'Please_answer_survey'}}">
<b>{{_ 'Please_answer_survey'}}</b>
</header>
<div class="content">

@ -0,0 +1,13 @@
Meteor.methods({
'livechat:changeLivechatStatus'() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-not-authorized', 'Not authorized');
}
const user = Meteor.user();
let newStatus = user.statusLivechat === 'available' ? 'not-available' : 'available';
Meteor.users.update(user._id, { $set: { statusLivechat: newStatus }});
}
});

@ -1,29 +1,3 @@
.calc(...) {
@process: ~`(function(e){function t(t,r){var a=");\n",c=n.split(","),i=c[0]+":"+t+"("+(c[1].trim()||0)+a;"start"==r?e="0;\n"+i:e+=i}e=e||8121991;var r="@{state}",n=e;if(8121991==e)return e;switch(r){case"1":t("-webkit-calc","start"),t("-moz-calc"),t("calc");break;case"2":t("-webkit-calc","start"),t("-moz-calc");break;case"3":t("-webkit-calc","start"),t("calc");break;case"4":t("-webkit-calc","start");break;case"5":t("-moz-calc","start"),t("calc");break;case"6":t("-moz-calc","start");break;case"7":t("calc","start")}return e=e.replace(/;$/g,"")})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`;
@state: 1; -lh-property: @process;
}
.transition(...) {
@process_webkit: ~`(function(e){e=e||"all 0 ease 0";var r=["background-size","border-radius","border-bottom-left-radius","border-bottom-right-radius","border-top-left-radius","border-top-right-radius","box-shadow","column","transform","filter"],t="-webkit-",n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`;
@process_moz: ~`(function(e){e=e||"all 0 ease 0";var r=["background-size","box-shadow","column","transform","filter"],t="-moz-",n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`;
@process_opera: ~`(function(e){e=e||"all 0 ease 0";var r=["transform"],t="-o-",n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;return/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,"")),r.forEach(function(r){-1!==e.indexOf(r)&&(e=e.replace(new RegExp(r,"g"),function(e){return t+e}))}),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`;
@process: ~`(function(e){e=e||"all 0 ease 0";var r=["-webkit-","-moz-","-o-",""],t=["column","transform","filter"],n=/(?:\d)(?:ms|s)/gi,a=/(?:\s|^)(\.?\d+\.?\d*)(?![^(]*\)|\w|%)/gi;/^[^, ]*,/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,""));var c=e.split(/(?:,)(?![^(]*\))/g);return c.forEach(function(e,n){t.forEach(function(t){-1!==e.indexOf(t)&&(c[n]="",r.forEach(function(a,u){c[n]+=e.trim().replace(new RegExp(t,"g"),function(e){return a+e}),u<r.length-1&&(c[n]+=",")}))})}),e=c.join(","),n.test(e)||"0"===e||(e=e.replace(a,function(e){return e+=parseFloat(e,10)>10?"ms":"s"})),e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`;
-webkit-transition: @process_webkit;
-moz-transition: @process_moz;
-o-transition: @process_opera;
transition: @process;
}
.transform(...) {
@process: ~`(function(e){e=e||"none";var r={translate:"px",rotate:"deg",rotate3d:"deg",skew:"deg"};/^\w*\(?[a-z0-9.]*\)?/.test(e)&&(e=e.replace(/(?:,)(?![^(]*\))/g,""));for(var t in r)e.indexOf(t)>=0&&(e=e.replace(new RegExp(t+"[\\w]?\\([a-z0-9, %]*\\)"),function(e){var n=/(\d+\.?\d*)(?!\w|%)/g;return"rotate3d"==t&&(n=/,\s*\d+$/),e.replace(n,function(e){return e+r[t]})}));return e})((function(){var e="@{arguments}";return e=e.replace(/^\[|\]$/g,"")})())`;
-webkit-transform: @process;
-moz-transform: @process;
-o-transform: @process;
-ms-transform: @process;
transform: @process;
}
.flex-list {
.active {
background-color: rgba(255,255,255,0.075);
@ -505,3 +479,25 @@
}
}
}
.livechat-section {
opacity: 0.5;
.transition(opacity .4s ease);
&.available {
opacity: 1;
}
}
.livechat-status {
float: right;
margin-right: 10px;
font-size: 20px;
&.available {
color: @status-online;
}
&.not-available {
color: @status-offline;
}
}

@ -1,12 +1,18 @@
<template name="livechat">
<h3 class="{{isActive}}">
{{_ "Livechat"}}
</h3>
<ul>
{{#each rooms}}
{{> chatRoomItem }}
{{else}}
<p class="empty">{{_ "No_livechats" }}</p>
{{/each}}
</ul>
<div class="livechat-section {{livechatAvailable}}">
<h3 class="{{isActive}}">
{{_ "Livechat"}}
{{#with available}}
<i class="livechat-status {{status}} {{icon}}" title="{{hint}}"></i>
{{/with}}
</h3>
<ul>
{{#each rooms}}
{{> chatRoomItem }}
{{else}}
<p class="empty">{{_ "No_livechats" }}</p>
{{/each}}
</ul>
</div>
</template>

@ -1,5 +1,5 @@
Template.livechat.helpers({
isActive: function() {
isActive() {
if (ChatSubscription.findOne({
t: 'l',
f: {
@ -15,7 +15,7 @@ Template.livechat.helpers({
return 'active';
}
},
rooms: function() {
rooms() {
var query = {
t: 'l',
open: true
@ -35,8 +35,30 @@ Template.livechat.helpers({
'name': 1
}
});
},
available() {
const user = Meteor.user();
return {
status: user.statusLivechat,
icon: user.statusLivechat === 'available' ? 'icon-toggle-on' : 'icon-toggle-off',
hint: user.statusLivechat === 'available' ? t('Available') : t('Not_Available')
};
},
livechatAvailable() {
const user = Meteor.user();
if (user) {
return user.statusLivechat;
}
}
});
Template.livechat.events({
'click .livechat-status'() {
Meteor.call('livechat:changeLivechatStatus', (err /*, results*/) => {
if (err) {
return toastr.error(t(err.reason));
}
});
}
});

@ -48,6 +48,8 @@ Package.onUse(function(api) {
api.addFiles('client/collections/LivechatPageVisited.js', 'client');
api.addFiles('client/collections/LivechatTrigger.js', 'client');
api.addFiles('client/methods/changeLivechatStatus.js', 'client');
// client views
api.addFiles('client/views/app/livechatAppearance.html', 'client');
api.addFiles('client/views/app/livechatAppearance.js', 'client');
@ -79,6 +81,7 @@ Package.onUse(function(api) {
// methods
api.addFiles('server/methods/addAgent.js', 'server');
api.addFiles('server/methods/addManager.js', 'server');
api.addFiles('server/methods/changeLivechatStatus.js', 'server');
api.addFiles('server/methods/pageVisited.js', 'server');
api.addFiles('server/methods/registerGuest.js', 'server');
api.addFiles('server/methods/removeAgent.js', 'server');

@ -15,7 +15,9 @@ Meteor.methods({
}
if (RocketChat.authz.addUserRoles(user._id, 'livechat-agent')) {
return RocketChat.models.Users.setOperator(user._id, true);
RocketChat.models.Users.setOperator(user._id, true);
RocketChat.models.Users.setLivechatStatus(user._id, 'available');
return true;
}
return false;

@ -0,0 +1,13 @@
Meteor.methods({
'livechat:changeLivechatStatus'() {
if (!Meteor.userId()) {
throw new Meteor.Error('error-not-authorized', 'Not authorized');
}
const user = Meteor.user();
let newStatus = user.statusLivechat === 'available' ? 'not-available' : 'available';
return RocketChat.models.Users.setLivechatStatus(user._id, newStatus);
}
});

@ -15,7 +15,9 @@ Meteor.methods({
}
if (RocketChat.authz.removeUserFromRoles(user._id, 'livechat-agent')) {
return RocketChat.models.Users.setOperator(user._id, false);
RocketChat.models.Users.setOperator(user._id, false);
RocketChat.models.Users.setLivechatStatus(user._id, 'not-available');
return true;
}
return false;

@ -33,7 +33,8 @@ RocketChat.models.Users.findOnlineAgents = function() {
*/
RocketChat.models.Users.findOnlineUserFromList = function(userList) {
var query = {
status: 'online',
statusConnection: { $ne: 'offline' },
statusLivechat: 'available',
username: {
$in: [].concat(userList)
}
@ -48,7 +49,8 @@ RocketChat.models.Users.findOnlineUserFromList = function(userList) {
*/
RocketChat.models.Users.getNextAgent = function() {
var query = {
status: 'online',
statusConnection: { $ne: 'offline' },
statusLivechat: 'available',
roles: 'livechat-agent'
};
@ -102,3 +104,21 @@ RocketChat.models.Users.findVisitorByToken = function(token) {
return this.find(query);
};
/**
* Change user's livechat status
* @param {string} token - Visitor token
*/
RocketChat.models.Users.setLivechatStatus = function(userId, status) {
let query = {
'_id': userId
};
let update = {
$set: {
'statusLivechat': status
}
};
return this.update(query, update);
};

@ -19,3 +19,4 @@ Meteor.publish 'userData', ->
requirePasswordChange: 1
requirePasswordChangeReason: 1
'services.password.bcrypt': 1
statusLivechat: 1 # @TODO create an API so a package could add fields here

Loading…
Cancel
Save