Skip to content
Snippets Groups Projects
Commit 3930c7ab authored by Diego Sampaio's avatar Diego Sampaio
Browse files

Merge pull request #1637 from RocketChat/livechat-manager

Livechat manager
parents 9b4876c5 5a4f1f77
No related branches found
No related tags found
No related merge requests found
Showing
with 523 additions and 23 deletions
......@@ -34,3 +34,4 @@ mizzao:timesync
reactive-var
accounts-password
standard-minifiers
tap:i18n
accounts-base@1.2.2
accounts-password@1.1.4
aldeed:simple-schema@1.3.3
arunoda:streams@0.1.17
babel-compiler@5.8.24_1
babel-runtime@0.1.4
......@@ -11,6 +12,7 @@ boilerplate-generator@1.0.4
caching-compiler@1.0.0
caching-html-compiler@1.0.2
callback-hook@1.0.4
cfs:http-methods@0.0.30
check@1.1.0
coffeescript@1.0.11
cosmos:browserify@0.9.2
......@@ -39,6 +41,7 @@ livedata@1.0.15
localstorage@1.0.5
logging@1.0.8
meteor@1.1.10
meteorspark:util@0.2.0
minifiers@1.1.7
minimongo@1.0.10
mizzao:timesync@0.3.4
......@@ -50,6 +53,7 @@ npm-mongo@1.4.39_1
observe-sequence@1.0.7
ordered-dict@1.0.4
promise@0.5.1
raix:eventemitter@0.1.3
random@1.0.5
rate-limit@1.0.0
reactive-dict@1.1.3
......@@ -63,6 +67,7 @@ spacebars@1.0.7
spacebars-compiler@1.0.7
srp@1.0.4
standard-minifiers@1.0.2
tap:i18n@1.7.0
templating@1.1.5
templating-tools@1.0.0
tracker@1.0.9
......
......@@ -9,4 +9,7 @@ FlowRouter.route '/livechat',
]
action: ->
BlazeLayout.render 'main', {center: 'room'}
if Meteor.userId()
BlazeLayout.render 'main', {center: 'room'}
else
BlazeLayout.render 'main', {center: 'register'}
......@@ -320,6 +320,66 @@ input:focus {
}
}
.livechat-registration {
position: fixed;
top: 0;
bottom: 0;
.title {
border-top-right-radius: 5px;
border-top-left-radius: 5px;
color: #FFF;
position: fixed;
top: 0;
width: 100%;
height: @header-min-height;
display: table;
z-index: 10;
cursor: pointer;
h1 {
margin: 0;
padding: 0 5px;
font-size: 9pt;
display: table-cell;
vertical-align: middle;
}
}
.livechat-form {
display: block;
background-color: #FFF;
top: @header-min-height;
bottom: @footer-min-height;
border-left: 1px solid #E7E7E7;
border-right: 1px solid #E7E7E7;
position: fixed;
width: 100%;
.wrapper {
height: 100%;
overflow-y: auto;
padding-bottom: 6px;
input, button {
display: block;
padding: 5px;
margin: 5px;
}
}
.error {
bottom: 50px;
position: fixed;
width: 100%;
background-color: #F7D799;
padding: 5px;
.transition(transform 0.3s ease-out);
.transform(translateY(200%));
&.show {
.transform(translateY(8px));
}
}
}
}
@media all and(max-height: 200px) {
.livechat-room {
.title {
......
Template.register.helpers
error: ->
return Template.instance().error.get()
title: ->
return '' unless Template.instance().subscriptionsReady()
return Settings.findOne('Livechat_title')?.value or 'Rocket.Chat'
color: ->
return 'transparent' unless Template.instance().subscriptionsReady()
return Settings.findOne('Livechat_title_color')?.value or '#C1272D'
welcomeMessage: ->
return ""
Template.register.events
'submit #livechat-registration': (e, instance) ->
e.preventDefault()
$name = instance.$('input[name=name]')
$email = instance.$('input[name=email]')
unless $name.val().trim() and $email.val().trim()
instance.error.set(TAPi18n.__('Please_fill_name_and_email'))
else
Meteor.call 'registerGuest', visitor.getToken(), $name.val(), $email.val(), (error, result) ->
if error?
return instance.error.set error.reason
Meteor.loginWithPassword result.user, result.pass, (error) ->
if error
return instance.error.set error.reason
BlazeLayout.render 'main', {center: 'room'}
'click .error': (e, instance) ->
instance.error.set()
Template.register.onCreated ->
@subscribe 'settings', ['Livechat_title', 'Livechat_title_color']
@error = new ReactiveVar
<template name="register">
<div class="livechat-registration">
<div class="title" style="background-color:{{color}}">
<h1>{{title}}</h1>
</div>
<form id="livechat-registration" class="livechat-form">
<div class="wrapper">
<label>
{{{welcomeMessage}}}
</label>
<input type="text" name="name" id="guestName" placeholder="{{_ "Name"}}">
<input type="email" name="email" id="guestEmail" placeholder="{{_ "E-mail"}}">
<button type="submit" id="btnEntrar" class="-btn"> {{_ "Start_Chat"}} </button>
</div>
{{#if error}}
<div class="error show">
<span>{{error}}</span>
</div>
{{/if}}
</form>
</div>
</template>
......@@ -49,6 +49,9 @@ Template.room.onCreated ->
self.atBottom = true
Template.room.onRendered ->
unless Meteor.userId()
return BlazeLayout.render 'main', {center: 'register'}
this.chatMessages = new ChatMessages
this.chatMessages.init(this.firstNode)
......
{
"E-mail": "E-mail",
"Name": "Name",
"Message": "Message",
"Please_fill_name_and_email": "Please fill name and e-mail",
"Start_Chat": "Start Chat"
}
this.LivechatDepartment = new Mongo.Collection('rocketchat_livechat_department');
......@@ -9,3 +9,24 @@ FlowRouter.route('/live/:name', {
triggersExit: [roomExit]
});
FlowRouter.route('/livechat-manager/departments', {
name: 'livechat-departments',
action: function(params, queryParams) {
BlazeLayout.render('main', { center: 'pageContainer', pageTemplate: 'livechatDepartments', pageTitle: t('Departments') });
}
});
FlowRouter.route('/livechat-manager/department/:_id?', {
name: 'livechat-department',
action: function(params, queryParams) {
if (params._id) {
pageTitle = t('Edit_Department');
} else {
pageTitle = t('New_Department');
}
BlazeLayout.render('main', { center: 'pageContainer', pageTemplate: 'livechatDepartmentForm', pageTitle: pageTitle});
}
});
......@@ -20,18 +20,15 @@ RocketChat.roomTypes.add('l', 5, {
AccountBox.addItem({
name: 'Livechat',
icon: 'icon-chat-empty',
class: 'livechat-manager',
route: {
name: 'livechat-manager',
path: '/livechat-manager',
action(params, queryParams) {
Session.set('openedRoom');
BlazeLayout.render('main', {
center: 'pageContainer',
pageTitle: t('Livechat_Manager'),
pageTemplate: 'livechatManager'
});
}
},
permissions: ['view-livechat-manager']
href: 'livechat-manager',
sideNav: 'livechatFlex',
permissions: ['view-livechat-manager'],
});
AccountBox.addRoute({
name: 'livechat-manager',
path: '/livechat-manager',
sideNav: 'livechatFlex',
pageTitle: t('Livechat_Manager'),
pageTemplate: 'livechatManager'
});
<template name="livechatDepartmentForm">
<form id="department-form" data-id="{{department._id}}">
<div class="rocket-form">
{{#if Template.subscriptionsReady}}
<fieldset>
<div class="input-line">
<label>{{_ "Enabled"}}</label>
<div>
<label><input type="radio" name="enabled" value="1" checked="{{$eq department.enabled true}}" /> {{_ "Yes"}}</label>
<label><input type="radio" name="enabled" value="0" checked="{{$eq department.enabled false}}" /> {{_ "No"}}</label>
</div>
</div>
<div class="input-line">
<label>{{_ "Name"}}</label>
<div>
<input type="text" name="name" value="{{department.name}}" placeholder="{{_ "Name"}}" />
</div>
</div>
<div class="input-line">
<label>{{_ "Description"}}</label>
<div>
<textarea name="description" rows="6">{{department.description}}</textarea>
</div>
</div>
<hr />
<h2>{{_ "Agents"}}</h2>
<div class="input-line double-col">
<input type="text" name="agent" placeholder="{{_ "Enter_a_username"}}">
<button name="addAgent" type="button" class="button add-agent">{{_ "Add_agent"}}</button>
</div>
<div class="list">
<table>
<thead>
<tr>
<th width="25%">{{_ "Username"}}</th>
<th>{{_ "Delete"}}</th>
</tr>
</thead>
<tbody>
{{#if agents.length}}
{{#each agents}}
<tr class="agent-info" data-id="{{_id}}">
<td>{{username}}</td>
<td><a href="#remove" class="remove-agent"><i class="icon-trash"></i></a></td>
</tr>
{{/each}}
{{else}}
<tr>
<td colspan="2">{{_ "There_are_no_agents_added_to_this_department_yet"}}</td>
</tr>
{{/if}}
</tbody>
</table>
</div>
</fieldset>
<div class="submit">
<button type="button" class="button secondary back"><i class="icon-left-big"></i><span>{{_ "Back"}}</span></button>
<button class="button save"><i class="icon-floppy"></i><span>{{_ "Save"}}</span></button>
</div>
{{else}}
{{> loading}}
{{/if}}
</div>
</form>
</template>
Template.livechatDepartmentForm.helpers({
department() {
// return Template.instance().department && !_.isEmpty(Template.instance().department.get()) ? Template.instance().department.get() : { enabled: true };
return Template.instance().department.get();
},
agents() {
return Template.instance().department && !_.isEmpty(Template.instance().department.get()) ? Template.instance().department.get().agents : []
}
});
Template.livechatDepartmentForm.events({
'submit #department-form' (e, instance) {
e.preventDefault();
var $btn = instance.$('button.save');
var _id = $(e.currentTarget).data('id');
var enabled = instance.$('input[name=enabled]:checked').val()
var name = instance.$('input[name=name]').val()
var description = instance.$('textarea[name=description]').val()
if (enabled !== "1" && enabled !== "0") {
return toastr.error(t('Please_select_enabled_yes_or_no'));
}
if (name.trim() === '') {
return toastr.error(t('Please_fill_a_name'));
}
var oldBtnValue = $btn.html();
$btn.html(t('Saving'));
agents = instance.department && !_.isEmpty(instance.department.get()) ? instance.department.get().agents : [];
departmentData = {
enabled: enabled === "1" ? true : false,
name: name.trim(),
description: description.trim(),
agents: agents
}
Meteor.call('livechat:saveDepartment', _id, departmentData, function(error, result) {
$btn.html(oldBtnValue);
if (error) {
return toastr.error(t(error.reason || error.error));
}
toastr.success(t('Saved'));
FlowRouter.go('livechat-departments');
});
},
'click button.back' (e, instance) {
e.preventDefault();
FlowRouter.go('livechat-departments');
},
'click button.add-agent' (e, instance) {
e.preventDefault();
var $btn = $(e.currentTarget);
var $agent = instance.$('input[name=agent]')
if ($agent.val().trim() === '') {
return toastr.error(t('Please_fill_a_username'));
}
var oldBtnValue = $btn.html();
$btn.html(t('Saving'));
Meteor.call('livechat:searchAgent', $agent.val(), function(error, user) {
$btn.html(oldBtnValue);
if (error) {
return toastr.error(t(error.reason || error.error));
}
department = instance.department.get() || {};
if (department.agents === undefined || !_.isArray(department.agents)) {
department.agents = [];
}
if (!_.findWhere(department.agents, { _id: user._id })) {
department.agents.push(user);
}
instance.department.set(department);
$agent.val('');
});
},
'click a.remove-agent' (e, instance) {
e.preventDefault();
department = instance.department.get();
department.agents = _.reject(department.agents, (agent) => { return agent._id === this._id });
instance.department.set(department);
},
'keydown input[name=agent]' (e, instance) {
if (e.keyCode === 13) {
e.preventDefault();
$("button.add-agent").click();
}
}
});
Template.livechatDepartmentForm.onCreated(function() {
this.department = new ReactiveVar({ enabled: true });
this.autorun(() => {
var sub = this.subscribe('livechat:departments', FlowRouter.getParam('_id'));
if (sub.ready()) {
department = LivechatDepartment.findOne({ _id: FlowRouter.getParam('_id') });
if (department) {
this.department.set(department);
}
}
});
});
<template name="livechatDepartments">
<div class="list">
<table>
<thead>
<tr>
<th width="25%">{{_ "Name"}}</th>
<th width="25%">{{_ "Description"}}</th>
<th width="25%">{{_ "Num_Agents"}}</th>
<th width="25%">{{_ "Enabled"}}</th>
<th>{{_ "Delete"}}</th>
</tr>
</thead>
<tbody>
{{#each departments}}
<tr class="department-info" data-id="{{_id}}">
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{numAgents}}</td>
<td>{{#if enabled}}{{_ "Yes"}}{{else}}{{_ "No"}}{{/if}}</td>
<td><a href="#remove" class="remove-department"><i class="icon-trash"></i></a></td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<form id="form-manager" class="inline">
<button name="newDepartment" class="button primary">{{_ "New_Department"}}</button>
</form>
</template>
Template.livechatDepartments.helpers({
"departments": () => {
return LivechatDepartment.find();
},
"numAgents"() {
if (Array.isArray(this.agents)) {
return this.agents.length;
}
}
});
Template.livechatDepartments.events({
"click button[name=newDepartment]": (event, instance) => {
event.preventDefault();
FlowRouter.go('livechat-department');
},
'click .remove-department' (e, instance) {
e.preventDefault();
e.stopPropagation();
swal({
title: t('Are_you_sure'),
type: 'warning',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: t('Yes'),
cancelButtonText: t('Cancel'),
closeOnConfirm: false,
html: false
}, () => {
Meteor.call('livechat:removeDepartment', this._id, function(error, result) {
if (error) {
return toastr.error(t(error.reason || error.error));
}
swal({
title: t('Removed'),
text: t('Department_removed'),
type: 'success',
timer: 1000,
showConfirmButton: false,
});
});
});
},
'click .department-info' (e, instance) {
e.preventDefault();
FlowRouter.go('livechat-department', { _id: this._id });
}
});
Template.livechatDepartments.onCreated(function() {
this.subscribe('livechat:departments');
});
<template name="livechatFlex">
<header>
<div>
<h4>{{_ "Livechat"}}</h4>
</div>
</header>
<div class="content">
<div class="wrapper">
<ul>
<li>
<a href="#link" class="admin-link">{{_ "Dashboard"}}</a>
<a href="#link" class="admin-link">{{_ "User_management"}}</a>
<a href="{{pathFor 'livechat-departments'}}" class="admin-link">{{_ "Departments"}}</a>
<a href="#link" class="admin-link">{{_ "Theme"}}</a>
<a href="#link" class="admin-link">{{_ "Integrations"}}</a>
<a href="#link" class="admin-link">{{_ "Live_sessions"}}</a>
</li>
</ul>
</div>
</div>
</template>
Template.livechatFlex.events({
'mouseenter header' () {
SideNav.overArrow()
},
'mouseleave header' () {
SideNav.leaveArrow()
},
'click header' () {
SideNav.closeFlex()
}
})
......@@ -4,7 +4,18 @@
"Add_manager" : "Add manager",
"Agent_added" : "Agent added",
"Agent_removed" : "Agent removed",
"Back" : "Back",
"Dashboard" : "Dashboard",
"Department_not_found" : "Department not found",
"Department_removed" : "Department removed",
"Departments" : "Departments",
"Description" : "Description",
"Edit_Department" : "Edit Department",
"Enable" : "Enable",
"Enabled" : "Enabled",
"Enter_a_username" : "Enter a username",
"Integrations" : "Integrations",
"Live_sessions" : "Live sessions",
"Livechat_agents" : "Livechat agents",
"Livechat_Manager" : "Livechat Manager",
"Livechat_managers" : "Livechat managers",
......@@ -12,6 +23,15 @@
"Livechat_title_color" : "Livechat Title Background Color",
"Manager_added" : "Manager added",
"Manager_removed" : "Manager removed",
"Name" : "Name",
"New_Department" : "New Department",
"Num_Agents" : "# Agents",
"Please_fill_a_name" : "Please fill a name",
"Please_fill_a_username" : "Please fill a username",
"Please_select_enabled_yes_or_no" : "Please select an option for Enabled",
"Saved" : "Saved",
"Theme" : "Theme",
"There_are_no_agents_added_to_this_department_yet" : "There are no agents added to this department yet.",
"User_management" : "User Management",
"Username_not_found" : "Username not found"
}
\ No newline at end of file
}
......@@ -24,6 +24,7 @@ Package.onUse(function(api) {
api.use('rocketchat:lib');
api.use('kadira:flow-router', 'client');
api.use('templating', 'client');
api.use('mongo');
api.addFiles('livechat.js', 'server');
api.addFiles('server/methods.js', 'server');
......@@ -38,21 +39,34 @@ Package.onUse(function(api) {
// client views
api.addFiles('client/views/app/livechatManager.html', 'client');
api.addFiles('client/views/app/livechatManager.js', 'client');
api.addFiles('client/views/app/livechatDepartments.html', 'client');
api.addFiles('client/views/app/livechatDepartments.js', 'client');
api.addFiles('client/views/app/livechatDepartmentForm.html', 'client');
api.addFiles('client/views/app/livechatDepartmentForm.js', 'client');
api.addFiles('client/views/sideNav/livechat.html', 'client');
api.addFiles('client/views/sideNav/livechat.js', 'client');
api.addFiles('client/views/sideNav/livechatFlex.html', 'client');
api.addFiles('client/views/sideNav/livechatFlex.js', 'client');
// methods
api.addFiles('server/methods/addAgent.js', 'server');
api.addFiles('server/methods/addManager.js', 'server');
api.addFiles('server/methods/saveDepartment.js', 'server');
api.addFiles('server/methods/searchAgent.js', 'server');
api.addFiles('server/methods/removeAgent.js', 'server');
api.addFiles('server/methods/removeManager.js', 'server');
api.addFiles('server/methods/removeDepartment.js', 'server');
// models
api.addFiles('server/models/Users.js', 'server');
api.addFiles('server/models/LivechatDepartment.js', 'server');
// collections
api.addFiles('client/lib/LivechatDepartment.js', 'client');
// publications
api.addFiles('server/publications/livechatAgents.js', 'server');
api.addFiles('server/publications/livechatManagers.js', 'server');
api.addFiles('server/publications/livechatDepartments.js', 'server');
api.addFiles('server/publications/visitorRoom.js', 'server');
// livechat app
......
Meteor.methods({
registerGuest: function(token) {
registerGuest: function(token, name, email) {
console.log('registerGuest ->'.green, token);
var pass, qt, user, userData, userExists, userId, inc = 0;
check(token, String);
......@@ -36,12 +36,19 @@ Meteor.methods({
password: pass
};
userId = Accounts.createUser(userData);
updateUser = {
name: name || user,
"profile.guest": true,
"profile.token": token
}
if (email && email.trim() !== "") {
updateUser.emails = [{ "address": email }];
}
Meteor.users.update(userId, {
$set: {
name: user,
"profile.guest": true,
"profile.token": token
}
$set: updateUser
});
return {
user: user,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment