Browse Source

Add Callcenter (Queues) submodule

pull/2/head
vbarkasov 8 years ago
parent
commit
88c700a46b
9 changed files with 1175 additions and 6 deletions
  1. +12
    -2
      README.md
  2. +4
    -4
      src/apps/callflows/app.js
  3. +36
    -0
      src/apps/callflows/i18n/en-US.json
  4. +171
    -0
      src/apps/callflows/submodules/callcenter/callcenter.css
  5. +834
    -0
      src/apps/callflows/submodules/callcenter/callcenter.js
  6. +30
    -0
      src/apps/callflows/views/callcenter-queue-callflow.html
  7. +63
    -0
      src/apps/callflows/views/callcenter-queue-edit.html
  8. +8
    -0
      src/apps/callflows/views/callcenter-queue.html
  9. +17
    -0
      src/apps/callflows/views/callcenter-user-row.html

+ 12
- 2
README.md View File

@ -1,2 +1,12 @@
# monster-ui-callflows-ng
Updated Callflows app
# Monster UI Callflows
#### Installation to source files:
1. Upload files from directory `src` to directory with source files of your Monster UI (*near the folders "apps", "css" and "js"*)
2. Register `callflows` app
3. Build your Monster UI with original builder (command `gulp`)
4. Activate the Callflows app in the Monster UI App Store ( `/#/apps/appstore` )
#### Installation to compiled files:
1. Upload all folders and files from directory `src` to root directory of your Monster UI (*near the folders "apps", "css" and "js"*)
2. Register `callflows` app
5. Activate Callflows app in the Monster UI App Store ( `/#/apps/appstore` )

+ 4
- 4
src/apps/callflows/app.js View File

@ -19,7 +19,8 @@ define(function(require){
'./submodules/user/user',
'./submodules/vmbox/vmbox',
'./submodules/featurecodes/featurecodes',
'./submodules/temporalset/temporalset'
'./submodules/temporalset/temporalset',
'./submodules/callcenter/callcenter'
]);
var app = {
@ -39,7 +40,7 @@ define(function(require){
'callflows.fetchActions': 'define_callflow_nodes'
},
subModules: ['misc', 'blacklist', 'conference', 'device', 'directory', 'faxbox', 'groups', 'media', 'menu', 'qubicle', 'resource', 'timeofday', 'user', 'vmbox', 'featurecodes', 'temporalset'],
subModules: ['misc', 'blacklist', 'conference', 'device', 'directory', 'faxbox', 'groups', 'media', 'menu', 'qubicle', 'resource', 'timeofday', 'user', 'vmbox', 'featurecodes', 'temporalset', 'callcenter'],
appFlags: {
flow: {},
@ -1479,8 +1480,7 @@ define(function(require){
action;
if (ui.draggable.hasClass('action')) {
action = ui.draggable.attr('name'),
action = ui.draggable.attr('name');
branch = self.branch(action);
branch.caption = self.actions[action].caption(branch, self.flow.caption_map);


+ 36
- 0
src/apps/callflows/i18n/en-US.json View File

@ -1092,6 +1092,42 @@
"label": "Toggle Do Not Disturb",
"tip": "Toggles Do Not Disturb"
}
},
"callcenter": {
"queue": "Queue",
"category": "Callcenter",
"tooltip": "Ask the caller to input the first letters of the name of the person that he wants to reach.",
"connect_caller_to_queue": "Connect caller to queue...",
"queue_edit_options": "Edit queue options",
"queue_create": "Create Queue",
"agentResume": "Agent Resume",
"agentBreak": "Agent Break",
"logoutAgent": "Logout Agent",
"loginAgent": "Login Agent",
"toggleAgent": "Toggle Agent",
"editQueue": "Edit Queue",
"createQueue": "Create Queue",
"basic": "Basic",
"advanced": "Advanced",
"options": "Options",
"name": "Name",
"namePlaceholder": "Name",
"nameDataContent": "Friendly name for this Queue",
"user": "User",
"actions": "Actions",
"addUser": "Add User",
"select": "Select",
"addThisUser": "Add this user to the queue",
"queueTimeoutPlaceholder": "Queue timeout (s)",
"queueTimeoutDataContent": "How long caller stays in queue, in seconds",
"queueTimeout": "Queue timeout (s)",
"memberTimeout": "Member timeout (s)",
"memberTimeoutPlaceholder": "Member timeout (s)",
"memberTimeoutDataContent": "How long to ring an agent's phone before moving to the next agent",
"delete": "Delete",
"save": "Save",
"thereIsCurrentlyNoUser": "There is currently no user in this queue",
"userLabel": "User"
}
},
"oldCallflows": {


+ 171
- 0
src/apps/callflows/submodules/callcenter/callcenter.css View File

@ -0,0 +1,171 @@
.callflows-port #user-form input[disabled=disabled] {
background: #ccc;
}
.callflows-port #user-form #equipment {
text-align: center;
font-size: 12px;
font-family: 'Helvetica';
}
.callflows-port #user-form #equipment .help_box {
color: #333333;
line-height: 12px;
}
.callflows-port #user-form #equipment .add_device {
margin-top: 15px;
display: inline-block;
}
.callflows-port #user-form #equipment .add_device:hover a {
color: #333333;
}
.callflows-port #user-form #equipment .add_device:hover .add_device_icon {
background-position: 0 -24px;
cursor: pointer;
}
.callflows-port #user-form #equipment #tab_devices {
border: 2px solid #666666;
border-radius: 15px 15px 15px 15px;
color: #666666;
margin: 20px auto auto;
width: 560px;
}
.callflows-port #user-form #equipment #tab_devices .action_device {
color: #666666;
cursor: pointer;
text-decoration: underline;
line-height: 30px;
}
.callflows-port #user-form #equipment #tab_devices .action_device:not(:first-child) {
margin-left: 10px;
}
.callflows-port #user-form #equipment #tab_devices .action_device:hover {
color: #05addc;
}
.callflows-port #user-form #equipment #tab_devices .column:not(.fifth) {
border-right: 1px solid #A6A6A6;
}
.callflows-port #user-form #equipment #tab_devices .column {
float: left;
text-align: center;
height: 30px;
line-height: 30px;
}
.callflows-port #user-form #equipment #tab_devices .column.first {
width: 190px;
overflow: hidden;
text-align:center;
}
.callflows-port #user-form #equipment #tab_devices .column.second {
width: 99px;
}
.callflows-port #user-form #equipment #tab_devices .column.third {
min-width: 49px;
}
.callflows-port #user-form #equipment #tab_devices .rows .column.third {
background-image: url('../../style/static/images/red_phone.png');
background-repeat: no-repeat;
background-position: 16px center;
}
.callflows-port #user-form #equipment #tab_devices .rows .column.third.registered,.callflows-port #user-form #equipment #tab_devices .rows .column.third.cellphone {
background-image: url('../../style/static/images/green_phone.png');
}
.callflows-port #user-form #equipment #tab_devices .rows .column.third.disabled {
background-image: url('../../style/static/images/gray_phone.png') !important;
}
.callflows-port #user-form #equipment #tab_devices .column.fourth {
width: 59px;
}
.callflows-port #user-form #equipment #tab_devices .column.fourth .enabled_checkbox {
margin-top: 8px;
}
.callflows-port #user-form #equipment #tab_devices .column.fifth {
width: 159px;
}
.callflows-port #user-form #equipment #tab_devices .column.merged {
width: 560px;
border-right: none;
}
.callflows-port #user-form #equipment #tab_devices .row {
height: 30px;
margin-left: 0px;
}
.callflows-port #user-form #equipment #tab_devices .row.title {
background-color: #dddddd !important;
border-bottom: 2px solid #666666 !important;
border-radius: 15px 15px 0px 0px;
font-weight: bold;
}
.callflows-port #user-form #equipment #tab_devices .row:not(:last-child) {
border-bottom: 1px solid #A6A6A6;
}
.callflows-port #user-form #equipment #tab_devices .row:last-child {
border-radius: 0px 0px 15px 15px;
}
.callflows-port #user-form #equipment #tab_devices .row:nth-child(odd) {
background-color: #eeeeee;
}
.callflows-port #user-form #equipment #tab_devices .row:nth-child(even) {
background-color: #dddddd;
}
.callflows-port #user-form #username_label {
color: #05addc;
text-align: left;
font-weight: bold;
}
.callflows-port #user-form .shoutcast-div {
display: none;
}
.callflows-port #user-form .shoutcast-div.active {
display: block;
}
/* Hotdesk devices */
.callflows-port #user-form #hotdesk_devices {
/*width: 400px;*/
width: 320px;
}
.callflows-port #user-form #hotdesk_devices .row .column:nth-child(1) {
width: 300px;
}
.callflows-port #user-form #hotdesk_devices .row .column:nth-child(2) {
width: 80px;
}
.callflows-port #user-form #hotdesk_devices .row .column.merged {
/* width: 400px; */
width: 320px;
}
.callflows-port #user-form #hotdesk_devices .row input[type="checkbox"] {
margin-top: 10px;
}

+ 834
- 0
src/apps/callflows/submodules/callcenter/callcenter.js View File

@ -0,0 +1,834 @@
define(function(require){
var $ = require('jquery'),
_ = require('underscore'),
toastr = require('toastr'),
monster = require('monster'),
timezone = require('monster-timezone');
var app = {
requests: {
'callcenter.queue.eavesdrop': {
'verb': 'PUT',
'url': 'accounts/{accountId}/queues/{queueId}/eavesdrop'
},
'callcenter.call.eavesdrop': {
'verb': 'PUT',
'url': 'accounts/{accountId}/queues/eavesdrop'
},
'callcenter.queues.list': {
'verb': 'GET',
'url': 'accounts/{accountId}/queues'
},
'callcenter.queues.create': {
'verb': 'PUT',
'url': 'accounts/{accountId}/queues'
},
'callcenter.queues.get': {
'verb': 'GET',
'url': 'accounts/{accountId}/queues/{queuesId}'
},
'callcenter.queues.update': {
'verb': 'POST',
'url': 'accounts/{accountId}/queues/{queuesId}'
},
'callcenter.queues.delete': {
'verb': 'DELETE',
'url': 'accounts/{accountId}/queues/{queuesId}'
},
'callcenter.queues.stats': {
'verb': 'GET',
'url': 'accounts/{accountId}/queues/stats'
},
'callcenter.agents.stats': {
'verb': 'GET',
'url': 'accounts/{accountId}/agents/stats'
},
'callcenter.agents.status': {
'verb': 'GET',
'url': 'accounts/{accountId}/agents/status'
},
'callcenter.agents.toggle': {
'verb': 'POST',
'url': 'accounts/{accountId}/agents/{agentId}/status'
},
'callcenter.agents.list': {
'verb': 'GET',
'url': 'accounts/{accountId}/agents'
},
'callcenter.agents.update': {
'verb': 'POST',
'url': 'accounts/{accountId}/queues/{queuesId}/roster'
}
},
validationRules: {
'#name': {
regex: /^.*/
},
'#connection_timeout': {
regex: /^[0-9]+$/
},
'#member_timeout': {
regex: /^[0-9]+$/
}/*,
'#caller_exit_key': {
regex: /^.{1}/
}*/
},
subscribe: {
//'callflows.queue.activate': 'activate', // TODO
'callflows.queue.edit': 'queueEdit',
'callflows.fetchActions': 'callcenterDefineActions',
'callflows.queue.popupEdit': 'queuePopupEdit'
},
random_id: false,
callcenterDefineActions: function(args) {
var self = this,
callflow_nodes= args.actions,
i18nApp = self.i18n.active().callflows.callcenter;
$.extend(callflow_nodes, {
'acdc_queue[id=*]': {
name: i18nApp.queue,
icon: 'link',
category: i18nApp.category,
module: 'acdc_queue',
tip: i18nApp.tooltip,
data: {
id: 'null'
},
rules: [
{
type: 'quantity',
maxSize: '1'
}
],
isUsable: 'true',
weight: 40,
caption: function(node, caption_map) {
var id = node.getMetadata('id'),
returned_value = '';
if(id in caption_map) {
returned_value = caption_map[id].name;
}
return returned_value;
},
edit: function(node, callback) {
self.getQueuesList(function(queues) {
var popup, popup_html;
popup_html = $(monster.template(self, 'callcenter-queue-callflow' , {
i18n: self.i18n.active(),
objects: {
items: queues,
selected: node.getMetadata('id') || ''
}
}));
if($('#queue_selector option:selected', popup_html).val() == undefined) {
$('#edit_link', popup_html).hide();
}
$('.inline_action', popup_html).click(function(ev) {
var _data = ($(this).data('action') == 'edit') ?
{ id: $('#queue_selector', popup_html).val() } : {};
ev.preventDefault();
self.queuePopupEdit({
data: _data,
callback: function(_data) {
node.setMetadata('id', _data.id || 'null');
node.caption = _data.name || '';
popup.dialog('close');
}
});
});
$('#add', popup_html).click(function() {
node.setMetadata('id', $('#queue_selector', popup).val());
node.caption = $('#queue_selector option:selected', popup).text();
popup.dialog('close');
});
popup = monster.ui.dialog(popup_html, {
title: self.i18n.active().callflows.callcenter.select_queue,
minHeight: '0',
beforeClose: function() {
if(typeof callback == 'function') {
callback();
}
}
});
});
},
listEntities: function(callback) {
monster.request({
resource: 'callcenter.queues.list',
data: {
accountId: self.accountId,
filters: { paginate:false }
},
success: function(data, status) {
callback && callback(data.data);
}
});
},
editEntity: 'callflows.queue.edit'
},
'acdc_agent[action=resume]': {
name: i18nApp.agentResume,
icon: 'reply',
category: i18nApp.category,
module: 'acdc_agent',
tip: '',
data: {
action: 'resume',
retries: '3'
},
rules: [
{
type: 'quantity',
maxSize: '0'
}
],
isUsable: 'true',
caption: function(node, caption_map) {
return '';
},
edit: function(node, callback) {
if(typeof callback == 'function') {
callback();
}
}
},
'acdc_agent[action=break]': {
name: i18nApp.agentBreak,
icon: 'chain-broken',
category: i18nApp.category,
module: 'acdc_agent',
tip: '',
data: {
action: 'break',
retries: '3'
},
rules: [
{
type: 'quantity',
maxSize: '0'
}
],
isUsable: 'true',
caption: function(node, caption_map) {
return '';
},
edit: function(node, callback) {
if(typeof callback == 'function') {
callback();
}
}
},
'acdc_agent[action=logout]': {
name: i18nApp.logoutAgent,
icon: 'sign-out',
category: i18nApp.category,
module: 'acdc_agent',
tip: '',
data: {
action: 'logout',
retries: '3'
},
rules: [
{
type: 'quantity',
maxSize: '0'
}
],
isUsable: 'true',
caption: function(node, caption_map) {
return '';
},
edit: function(node, callback) {
if(typeof callback == 'function') {
callback();
}
}
},
'acdc_agent[action=login]': {
name: i18nApp.loginAgent,
icon: 'sign-in',
category: i18nApp.category,
module: 'acdc_agent',
tip: '',
data: {
action: 'login',
retries: '3'
},
rules: [
{
type: 'quantity',
maxSize: '0'
}
],
isUsable: 'true',
caption: function(node, caption_map) {
return '';
},
edit: function(node, callback) {
if(typeof callback == 'function') {
callback();
}
}
},
'acdc_agent[action=toggle]': {
name: i18nApp.toggleAgent,
icon: 'toggle-off',
category: i18nApp.category,
module: 'acdc_agent',
tip: '',
data: {
action: 'toggle',
retries: '3'
},
rules: [
{
type: 'quantity',
maxSize: '0'
}
],
isUsable: 'true',
caption: function(node, caption_map) {
return '';
},
edit: function(node, callback) {
if(typeof callback == 'function') {
callback();
}
}
}
});
},
queuePopupEdit: function(args) {
var self = this,
popup_html = $('<div class="inline_popup callflows-queue"><div class="inline_content main_content"></div></div>'),
callback = args.callback,
popup,
data = args.data,
data_defaults = args.data_defaults;
popup_html.css({
height: 500,
'overflow-y': 'scroll'
});
self.queueEdit({
data: data,
parent: popup_html,
target: $('.inline_content', popup_html),
callbacks: {
save_success: function(_data) {
popup.dialog('close');
if(typeof callback == 'function') {
callback(_data);
}
},
delete_success: function() {
popup.dialog('close');
if(typeof callback == 'function') {
callback({ data: {} });
}
},
after_render: function() {
popup = monster.ui.dialog(popup_html, {
title: (data.id) ? self.i18n.active().callflows.callcenter.queue_edit : self.i18n.active().callflows.callcenter.queue_create
});
}
},
data_defaults: data_defaults
});
},
queueEdit: function(args) {
var self = this,
data = args.data,
parent = args.parent || $('#queue-content'),
target = args.target || $('#queue-view', parent),
_callbacks = args.callbacks || {},
callbacks = {
save_success: _callbacks.save_success || function(_data) {
// self.queueRenderList(parent);
self.queueEdit({
data: {
id: _data.data.id
},
parent: parent,
target: target,
callbacks: callbacks
});
},
save_error: _callbacks.save_error,
delete_success: _callbacks.delete_success || function() {
target.empty();
//self.queueRenderList(parent);
},
delete_error: _callbacks.delete_error,
after_render: _callbacks.after_render
},
defaults = {
data: $.extend(true, {
connection_timeout: '300',
member_timeout: '5'
/* caller_exit_key: '#' */
}, args.data_defaults || {}),
field_data: {
sort_by: {
'first_name': self.i18n.active().callflows.callcenter.first_name,
'last_name': self.i18n.active().callflows.callcenter.last_name
}
}
};
self.getUsersList(function(users) {
defaults.field_data.users = users;
if(typeof data == 'object' && data.id) {
monster.request({
resource: 'callcenter.queues.get',
data: {
accountId: self.accountId,
queuesId: data.id,
generateError: false
},
success: function (_data) {
var render_data = $.extend(true, defaults, _data);
render_data.field_data.old_list = [];
if('agents' in _data.data) {
render_data.field_data.old_list = _data.data.agents;
}
self.queueRender(render_data, target, callbacks);
if(typeof callbacks.after_render == 'function') {
callbacks.after_render();
}
}
});
} else {
self.queueRender(defaults, target, callbacks);
if(typeof callbacks.after_render == 'function') {
callbacks.after_render();
}
}
});
},
getUsersList: function(callback) {
var self = this;
self.callApi({
resource: 'user.list',
data: {
accountId: self.accountId,
filters: { paginate: false },
generateError: false
},
success: function(users, status) {
callback && callback(users.data);
}
});
},
queueRenderList: function(_parent){
var self = this,
parent = _parent || $('#queue-content'),
i18nApp = self.i18n.active().callflows.callcenter;
self.getQueuesList(function(data) {
var map_crossbar_data = function(data) {
var new_list = [];
if(data.length > 0) {
$.each(data, function(key, val) {
new_list.push({
id: val.id,
title: val.name || _t('voip_queue', 'no_name')
});
});
}
new_list.sort(function(a, b) {
return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1;
});
return new_list;
};
// TODO!
/*$('#queue-listpanel', parent)
.empty()
.listpanel({
label: i18nApp.queues_label,
identifier: 'queue-listview',
new_entity_label: i18nApp.add_queue_label,
data: map_crossbar_data(data.data),
publisher: winkstart.publish,
notifyMethod: 'queue.edit',
notifyCreateMethod: 'queue.edit',
notifyParent: parent
});*/
});
},
getQueuesList: function(callback) {
var self = this;
monster.request({
resource: 'callcenter.queues.list',
data: {
accountId: self.accountId,
generateError: false
},
success: function (data) {
callback && callback(data.data);
}
});
},
// TODO
/*activate: function(parent) {
var self = this,
queue_html = $(monster.template(self, 'callcenter-queue'));
(parent || $('#monster_content'))
.empty()
.append(queue_html);
self.queueRenderList(queue_html);
},*/
queueRender: function(data, target, callbacks) {
var self = this;
data.i18nApp = self.i18n.active().callflows.callcenter;
var queue_html = $(monster.template(self, 'callcenter-queue-edit' , data));
self.userListRender(data, queue_html);
var $form = queue_html.find('form');
monster.ui.validate($form, {
rules: self.validationRules
});
monster.ui.tooltips(queue_html, {
selector: '[rel=popover]'
});
$('.queue-save', queue_html).click(function(ev) {
ev.preventDefault();
if(monster.ui.valid($form)) {
var form_data = monster.ui.getFormData($form);
var new_list = [];
$('.rows .row:not(#row_no_data)', queue_html).each(function() {
new_list.push($(this).dataset('id'));
});
data.field_data.user_list = {
old_list: data.data.agents || [],
new_list: new_list
};
self.queueSave(form_data, data, callbacks.save_success, callbacks.save_error);
} else {
toastr.error(self.i18n.active().callflows.callcenter.formHasErrorsMessage);
}
});
$('.queue-delete', queue_html).click(function(ev) {
ev.preventDefault();
monster.ui.confirm(self.i18n.active().callflows.callcenter.deleteConfirmMessage, function() {
self.queueDelete(data, callbacks.delete_success, callbacks.delete_error);
});
});
$('.js-add-user-btn', queue_html).click(function(e) {
e.preventDefault();
var $userSelect = $('#users-list', queue_html);
if($userSelect.val() != 'empty_option_user') {
var user_id = $userSelect.val(),
user_data = {
user_id: user_id,
user_name: $('#option_user_' + user_id, queue_html).text()
};
if($('#row_no_data', queue_html).size() > 0) {
$('#row_no_data', queue_html).remove();
}
$('.js-user-table-body', queue_html).prepend($(monster.template(self, 'callcenter-user-row' , user_data)));
$('#option_user_'+user_id, queue_html).hide();
$userSelect.val('empty_option_user');
}
});
$(queue_html).on('click', '.js-edit-user', function() {
var _data = {
id: $(this).data('id')
};
monster.pub('callflows.user.popupEdit', {
data: _data,
callflow: function(_data) {
$('#row_user_' + _data.data.id + ' .column.first', queue_html).html(_data.data.first_name + ' ' + _data.data.last_name);
$('#option_user_' + _data.data.id, queue_html).html(_data.data.first_name + ' ' + _data.data.last_name);
}
});
});
$(queue_html).on('click', '.js-delete-user', function() {
var user_id = $(this).data('id');
//removes it from the grid
$('#row_user_' + user_id, queue_html).remove();
//re-add it to the dropdown
$('#option_user_'+user_id, queue_html).show();
//if grid empty, add no data line
if($('.js-user-table-body .js-user-table-item', queue_html).size() == 0) {
$('.js-user-table-body', queue_html).append($(monster.template(self, 'callcenter-user-row')));
}
});
target.empty().append(queue_html);
},
queueDelete: function(data, success, error) {
var self = this;
if(typeof data.data == 'object' && data.data.id) {
self.callApi({
resource: 'callcenter.queues.delete',
data: {
accountId: self.accountId,
queuesId: data.data.id,
generateError: false
},
success: function(_data, status) {
if(typeof success == 'function') {
success(_data, status);
}
},
error: function(_data, status) {
if(typeof error == 'function') {
error(_data, status);
}
}
});
}
},
userListRender: function(data, parent) {
var self = this;
if(data.data.id) {
if('agents' in data.data && data.data.agents.length > 0) {
var user_item;
$.each(data.field_data.users, function(k, v) {
if(data.data.agents.indexOf(v.id) >= 0) {
var html = $(monster.template(self, 'callcenter-user-row' , {
user_id: v.id,
user_name: v.first_name + ' ' + v.last_name
}));
$('.js-user-table-body', parent).append(html);
$('#option_user_' + v.id, parent).hide();
}
});
} else {
$('.js-user-table-body', parent).empty()
.append($(monster.template(self, 'callcenter-user-row')));
}
} else {
$('.js-user-table-body', parent).empty()
.append($(monster.template(self, 'callcenter-user-row')));
}
},
queueSave: function(form_data, data, success, error) {
var self = this,
normalized_data = self.normalizeData($.extend(true, {}, data.data, form_data));
if (typeof data.data == 'object' && data.data.id) {
monster.request({
resource: 'callcenter.queues.update',
data: {
accountId: self.accountId,
queuesId: data.id,
generateError: false,
data: normalized_data
},
success: function (_data, status) {
if(typeof success == 'function') {
self.usersUpdate(data.field_data.user_list, _data.data.id, function() {
success(_data, status, 'update');
});
}
},
error: function(_data, status) {
if(typeof error == 'function') {
error(_data, status, 'update');
}
}
});
} else {
monster.request({
resource: 'callcenter.queues.create',
data: {
accountId: self.accountId,
generateError: false,
data: normalized_data
},
success: function (_data, status) {
if(typeof success == 'function') {
self.usersUpdate(data.field_data.user_list, _data.data.id, function() {
success(_data, status, 'create');
});
}
},
error: function(_data, status) {
if(typeof error == 'function') {
error(_data, status, 'update');
}
}
});
}
},
usersUpdate: function(data, queue_id, success) {
var old_queue_user_list = data.old_list,
new_queue_user_list = data.new_list,
self = this,
users_updated_count = 0,
users_count = 0,
callback = function() {
users_updated_count++;
if(users_updated_count >= users_count) {
success();
}
};
if(old_queue_user_list) {
$.each(old_queue_user_list, function(k, v) {
if(new_queue_user_list.indexOf(v) === -1) {
//Request to update user without this queue.
users_count++;
self.singleUserUpdate(v, queue_id, 'remove', callback);
}
});
$.each(new_queue_user_list, function(k, v) {
if(old_queue_user_list.indexOf(v) === -1) {
users_count++;
self.singleUserUpdate(v, queue_id, 'add', callback);
}
});
} else {
if(new_queue_user_list) {
$.each(new_queue_user_list, function(k, v) {
users_count++;
self.singleUserUpdate(v, queue_id, 'add', callback);
});
}
}
/* If no users has been updated, we still need to refresh the view for the other attributes */
if(users_count == 0) {
success();
}
},
singleUserUpdate: function(user_id, queue_id, action, callback) {
var self = this;
self.callApi({
resource: 'user.get',
data: {
accountId: self.accountId,
userId: user_id,
generateError: false
},
success: function(_data, status) {
if(action =='add') {
if(!_data.data.queues || typeof _data.data.queues != 'object') {
_data.data.queues = [];
}
_data.data.queues.push(queue_id);
/* If a user is added to a queue, but is not enabled as an agent, we enable this user automatically */
if(!('queue_pin' in _data.data)) {
_data.data.queue_pin = '';
}
} else { //remove
_data.data.queues.splice(_data.data.queues.indexOf(queue_id), 1);
}
self.callApi({
resource: 'user.update',
data: {
accountId: self.accountId,
userId: user_id,
data: _data.data,
generateError: false
},
success: function(_data, status) {
if(typeof callback === 'function') {
callback(status);
}
},
error: function(_data, status) {
if(typeof callback === 'function') {
callback(status);
}
}
});
}
});
},
normalizeData: function(form_data) {
delete form_data.users;
return form_data;
}
};
return app;
});

+ 30
- 0
src/apps/callflows/views/callcenter-queue-callflow.html View File

@ -0,0 +1,30 @@
<div>
<div class="dialog_popup">
<h1>{{ i18n.callflows.callcenter.connect_caller_to_queue }}</h1>
<form name="form" method="post" action="#">
<div class="form_content">
<div class="popup_field">
<label for="queue_selector">{{ i18n.callflows.callcenter.queue }}</label>
<div class="select_wrapper">
<select id="queue_selector">
{{#select objects.selected}}
{{#each objects.items}}
<option value="{{id}}">{{name}}</option>
{{/each}}
{{/select}}
</select>
<div class="action_links_div">
<div id="edit_link">
<a class="inline_action" data-action="edit" href="javascript:void(0);">{{ i18n.callflows.callcenter.queue_edit_options }}</a>
</div>
<div><a class="inline_action" data-action="create" href="#">{{ i18n.callflows.callcenter.queue_create }}</a></div>
</div>
</div>
</div>
</div>
</form>
<div class="buttons-center">
<button id="add" class="monster-button monster-button-primary">OK</button>
</div>
</div>
</div>

+ 63
- 0
src/apps/callflows/views/callcenter-queue-edit.html View File

@ -0,0 +1,63 @@
<div>
{{#if data.id }}
<h1>{{ i18nApp.editQueue }}</h1>
{{else}}
<h1>{{ i18nApp.createQueue }}</h1>
{{/if}}
<form id="queue-form" action="" method="post" class="form-vertical">
<div class="control-group">
<label class="control-label" for="name">{{ i18nApp.name }}</label>
<div class="controls">
<input id="name" name="name" type="text" data-placement="top" data-toggle="tooltip" placeholder="{{ i18nApp.namePlaceholder }}" value="{{ data.name}}" rel="popover" data-original-title="{{this.help}}" data-content="{{ i18nApp.nameDataContent }}" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="connection_timeout">{{ i18nApp.queueTimeout }}</label>
<div class="controls">
<input id="connection_timeout" name="connection_timeout" type="text" placeholder="{{ i18nApp.queueTimeoutPlaceholder }}" value="{{ data.connection_timeout }}" rel="popover" data-content="{{ i18nApp.queueTimeoutDataContent }}"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="member_timeout">{{ i18nApp.memberTimeout }}</label>
<div class="controls">
<input id="member_timeout" name="member_timeout" type="text" placeholder="{{ i18nApp.memberTimeoutPlaceholder }}" value="{{ data.member_timeout }}" rel="popover" data-content="{{ i18nApp.memberTimeoutDataContent }}"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="users-list">{{ i18nApp.userLabel }}</label>
<div class="controls">
<table class="table">
<thead>
<tr>
<th>{{ i18nApp.user }}</th>
<th>{{ i18nApp.actions }}</th>
</tr>
</thead>
<tbody class="js-user-table-body"></tbody>
</table>
<h4>{{ i18nApp.addUser }}</h4>
<select name="user_id" id="users-list">
<option id="empty_option_user" value="empty_option_user">-- {{ i18nApp.select }} --</option>
{{#each field_data.users}}
<option value="{{ id }}" id="option_user_{{ id }}">{{ first_name }} {{ last_name }}</option>
{{/each}}
</select>
<a class="js-add-user-btn" href="" data-queue_id="{{ data.id }}">
<i class="fa fa-arrow-up"></i>
{{ i18nApp.addThisUser }}
</a>
</div>
</div>
<div class="buttons-wrapper">
{{#if data.id}}
<button class="btn btn-danger queue-delete">{{ i18nApp.delete }}</button>
{{/if}}
<button class="btn btn-primary queue-save">{{ i18nApp.save }}</button>
</div>
</form>
</div>

+ 8
- 0
src/apps/callflows/views/callcenter-queue.html View File

@ -0,0 +1,8 @@
<div>
<div id="queue-content" class="container-fluid page">
<div id="queue-listpanel" class="sidebar list_panel"></div>
<div class="content">
<div id="queue-view" class="main_content"></div>
</div>
</div>
</div>

+ 17
- 0
src/apps/callflows/views/callcenter-user-row.html View File

@ -0,0 +1,17 @@
{{#if user_id}}
<tr class="js-user-table-item" id="row_user_{{ user_id }}" data-id="{{ user_id }}">
<td>{{ user_name }}</td>
<td>
<a class="js-edit-user" data-id="{{ user_id }}">
<i class="fa fa-pencil"></i>
</a>
<a class="js-delete-user" data-id="{{ user_id }}">
<i class="fa fa-trash"></i>
</a>
</td>
</tr>
{{else}}
<tr class="js-user-table-item" id="row_no_data">
<td colspan="2" >{{ i18n.callflows.callcenter.thereIsCurrentlyNoUser }}</td>
</tr>
{{/if}}

Loading…
Cancel
Save