Browse Source

Queues settings

- Create new queue behavior
master
Vladimir Barkasov 8 years ago
parent
commit
101c5c3b71
6 changed files with 722 additions and 39 deletions
  1. +364
    -27
      app.js
  2. +113
    -2
      style/app.css
  3. +13
    -10
      views/dashboard.html
  4. +47
    -0
      views/settings.html
  5. +181
    -0
      views/settings_queue_form.html
  6. +4
    -0
      views/settings_queues_list.html

+ 364
- 27
app.js View File

@ -11,16 +11,16 @@ var app = {
i18n: {
'en-US': { customCss: false },
'de-DE': { customCss: false },
'dk-DK': { customCss: false },
'it-IT': { customCss: false },
'fr-FR': { customCss: false },
'nl-NL': { customCss: false },
'ro-RO': { customCss: false },
'ru-RU': { customCss: false },
'pt-PT': { customCss: false },
'zh-CN': { customCss: false },
'es-ES': { customCss: false }
'de-DE': { customCss: false },
'dk-DK': { customCss: false },
'it-IT': { customCss: false },
'fr-FR': { customCss: false },
'nl-NL': { customCss: false },
'ro-RO': { customCss: false },
'ru-RU': { customCss: false },
'pt-PT': { customCss: false },
'zh-CN': { customCss: false },
'es-ES': { customCss: false }
},
load: function(callback){
@ -46,9 +46,47 @@ var app = {
app: self,
callback: callback
});
Handlebars.registerHelper('compare', function (lvalue, operator, rvalue, options) {
var operators, result;
if (arguments.length < 3) {
throw new Error('Handlerbars Helper \'compare\' needs 2 parameters');
}
if (options === undefined) {
options = rvalue;
rvalue = operator;
operator = '===';
}
operators = {
'==': function (l, r) { return l == r; },
'===': function (l, r) { return l === r; },
'!=': function (l, r) { return l != r; },
'!==': function (l, r) { return l !== r; },
'<': function (l, r) { return l < r; },
'>': function (l, r) { return l > r; },
'<=': function (l, r) { return l <= r; },
'>=': function (l, r) { return l >= r; },
'typeof': function (l, r) { return typeof l == r; }
};
if (!operators[operator]) {
throw new Error('Handlerbars Helper \'compare\' doesn\'t know the operator ' + operator);
}
result = operators[operator](lvalue, rvalue);
if (result) {
return options.fn(this);
} else {
return options.inverse(this);
}
});
},
// API Calls
queue_eavesdrop: function (callback) {
var self = this;
@ -62,7 +100,7 @@ var app = {
}
});
},
listDevices: function (callback) {
/* listDevices: function (callback) {
var self = this;
self.callApi({
@ -74,7 +112,7 @@ var app = {
callback(devices.data);
}
});
},
},*/
poll_agents: function (global_data, _container) {
var self = this,
@ -202,7 +240,7 @@ var app = {
});
},
get_queues_stats: function (callback) {
/*get_queues_stats: function (callback) {
var self = this;
self.callApi({
@ -214,7 +252,7 @@ var app = {
callback(queues_stats.data);
}
});
},
},*/
get_queues: function (callback) {
var self = this;
@ -244,7 +282,7 @@ var app = {
});
},
render_callwaiting_list: function (_container) {
/*render_callwaiting_list: function (_container) {
var self = this,
container = _container || $('#dashboard-content');
@ -254,7 +292,7 @@ var app = {
data: []
});
$('.add_flow', container).empty().html('call_waiting_log');
},
},*/
get_time_seconds: function (seconds) {
var seconds = Math.floor(seconds),
@ -469,14 +507,14 @@ var app = {
self.fetch_all_data(function(data) {
queues_html = $(monster.template(self, 'queues_dashboard', {queues: data.queues}));
agents_html = $(monster.template(self, 'agents_dashboard', {agents: data.agents}));
calls_html = $(monster.template(self, 'calls_dashboard', {progress: data.calls_in_progress, waits: data.calls_waiting} ));
var queues_html = $(monster.template(self, 'queues_dashboard', {queues: data.queues}));
var agents_html = $(monster.template(self, 'agents_dashboard', {agents: data.agents}));
var calls_html = $(monster.template(self, 'calls_dashboard', {progress: data.calls_in_progress, waits: data.calls_waiting} ));
html = $(monster.template(self, 'dashboard', {}));
var html = $(monster.template(self, 'dashboard', {}));
container.empty().append(html);
scroll_value = $('.topbar-right .list_queues_inner', container).scrollLeft() || 0;
var scroll_value = $('.topbar-right .list_queues_inner', container).scrollLeft() || 0;
container.find('#dashboard-view').empty().append(agents_html);
container.find('.topbar-right').empty().append(queues_html);
container.find('.topbar-right .list_queues_inner').animate({ scrollLeft: scroll_value }, 0);
@ -489,9 +527,309 @@ var app = {
if(typeof queue_id != 'undefined') {
self.detail_stat(queue_id, container);
}
self.dashboardBindEvents(container);
});
},
dashboardBindEvents: function($container) {
var self = this;
$container.find('.js-open-cc-settings').on('click', function(e) {
e.preventDefault();
var html = $(monster.template(self, 'settings', {}));
$container.empty().append(html);
self.settingsInit($container);
}).click(); // TODO: remove ".click()" after development
},
settingsInit: function($container) {
var self = this;
self.settingsRenderList($container, function() {
monster.ui.tooltips($container, {
options: {
placement: 'bottom',
container: 'body'
}
});
self.settingsBindEvents($container);
});
},
settingsBindEvents: function($container) {
var self = this;
$container.find('.js-open-cc-dashboard').on('click', function(e) {
e.preventDefault();
self.render($container);
});
$container.find('.js-cc-create-queue').on('click', function(e) {
e.preventDefault();
self.settingsQueueNewInit($container);
});
},
settingsQueueNewInit: function($container) {
var self = this;
var $parent = $container.find('#cc-settings-content');
var $queuesList = $('#queues-list');
$queuesList.find('.active').each(function(e, i) {
$(this).removeClass('active');
});
$queuesList.find('.js-new-queue-item').remove();
$queuesList.find('.js-nav-list').append('<li class="js-new-queue-item active"><a href="#">New Queue</a></li>');
self.settingsQueueFormRender($parent);
},
settingsQueueFormRender: function($container, data) {
var self = this;
var defaultQueueData = {
connection_timeout: '0',
member_timeout: '5',
agent_wrapup_time: '30',
record_caller: true,
moh: {},
notifications: {},
max_queue_size: '0'
};
data = $.extend(true, defaultQueueData, data || {});
/* var data = {
"data": {
"name": "test2",
"record_caller": true,
"moh": "1880ca1c34f3ed88b4071cda175f525e",
"strategy": "round_robin",
"call_recording_url": "",
"agent_wrapup_time": "30",
"max_queue_size": "0",
"connection_timeout": "0",
"enter_when_empty": false,
"notifications": {
"hangup": "",
"pickup": "",
"method": "GET"
},
"member_timeout": "5",
"ui_metadata": {
"ui": "kazoo-ui",
"version": "3.22-0https://github.com/2600hz/kazoo-ui.gittags/3.22.096a18351d378f041d957acd3c1c94c8e6de462ef2015-10-22_01-32-14120"
}
},
"verb": "PUT"
};*/
self.callApi({
resource: 'media.list',
data: {
accountId: self.accountId
},
success: function(mediaData, status) {
console.log('mediaData:');
console.log(mediaData);
mediaData.data.unshift(
{
id: '',
name: 'Default' // TODO: i18n it
},
{
id: 'silence_stream://300000',
name: 'Silence' // TODO: i18n it
}
);
var html = $(monster.template(self, 'settings_queue_form', {
data: data,
media_list: mediaData.data
}));
$container.empty().append(html);
self.settingsQueueFormBindEvents($container);
}
});
},
settingsQueueFormBindEvents: function($container) {
var self = this;
$container.find('.js-edit-media').on('click', function(e) {
e.preventDefault();
});
$container.find('.js-create-media').on('click', function(e) {
e.preventDefault();
});
$container.find('.js-save-queue').on('click', function(e) {
e.preventDefault();
var data = self.settingsQueueFormGetData($container.find('#queue-form'));
console.log('Queue form data:');
console.log(data);
self.settingsQueueSave(data, function(){
console.log('Queue saving complete!');
});
});
$container.find('.js-cancel').on('click', function(e) {
e.preventDefault();
});
},
settingsQueueFormGetData: function($form) {
var sourceData = $form.serializeArray();
var data = {};
for(var i=0, len=sourceData.length; i < len; i++) {
if(typeof(sourceData[i].value) === 'string' && sourceData[i].value === '') {
// is empty string value, to continue iteration
} else {
data[sourceData[i].name] = sourceData[i].value;
}
}
return data;
},
settingsQueueSave: function(queueData, callback) {
var self = this;
/*var data = {
"data": {
"connection_timeout": "0",
"member_timeout": "5",
"agent_wrapup_time": "30",
"record_caller": true,
"moh": "1880ca1c34f3ed88b4071cda175f525e",
"notifications": {"hangup": "", "pickup": "", "method": "GET"},
"max_queue_size": "0",
"name": "test2",
"strategy": "round_robin",
"call_recording_url": "",
"enter_when_empty": false,
"ui_metadata": {
"ui": "kazoo-ui",
"version": "3.22-0https://github.com/2600hz/kazoo-ui.gittags/3.22.096a18351d378f041d957acd3c1c94c8e6de462ef2015-10-22_01-32-14120"
}
},
"verb": "PUT"
};*/
if (typeof(queueData.data) == 'object'
&& queueData.data.hasOwnProperty('id')) {
// edit queue
// 'queue.update'
self.callApi({
resource: 'queues.queues_get',
data: {
accountId: self.accountId,
queuesId: queueData.data.id
},
success: function(data, status) {
console.log(data);
}
});
} else {
// create new queue
// 'queue.create'
self.callApi({
resource: 'queues.queues_create',
data: {
accountId: self.accountId,
data: queueData
},
success: function(data, status) {
console.log(data);
}
});
}
},
settingsRenderList: function($container, callback) {
var self = this;
self.callApi({
resource: 'queues.queues_list',
data: {
accountId: self.accountId
},
success: function (data, status) {
console.log(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('queue', 'no_name')
});
});
}
new_list.sort(function(a, b) {
return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1;
});
return new_list;
};
var queuesListHtml = $(monster.template(self, 'settings_queues_list', {
}));
$container.find('#queues-list').empty().append(queuesListHtml);
if(typeof(callback) !== 'undefined') {
callback();
}
/*$('#queue-listpanel', parent)
.empty()
.listpanel({
label: 'Queues',
identifier: 'queue-listview',
new_entity_label: _t('queue', 'add_acd'),
data: map_crossbar_data(data.data),
publisher: winkstart.publish,
notifyMethod: 'queue.edit',
notifyCreateMethod: 'queue.edit',
notifyParent: parent
});*/
/*if(typeof(callback) === 'function') {
callback();
}*/
}
});
/* winkstart.request(true, 'queue.list', {
account_id: winkstart.apps['call_center'].account_id,
api_url: winkstart.apps['call_center'].api_url
}
);*/
},
fetch_all_data: function(callback) {
var self = this;
@ -586,7 +924,6 @@ var app = {
$v.show();
}
});
},
clean_timers: function() {
@ -606,7 +943,7 @@ var app = {
self.map_timers = {};
},
activate_queue_stat: function(args) {
/*activate_queue_stat: function(args) {
//TODO check render global data
var self = this,
container = args.container || $('#monster-content');
@ -615,9 +952,9 @@ var app = {
var $self_queue = $('#'+args.id, container);
self.detail_stat(args.id, container);
});
},
},*/
activate: function(_container) {
/*activate: function(_container) {
var self = this,
container = _container || $('#monster-content');
container.empty();
@ -625,7 +962,7 @@ var app = {
self.hide_logout = false;
//TODO check render global data
self.render(container);
},
},*/
login: function(agent, callback) {
var self = this,


+ 113
- 2
style/app.css View File

@ -659,6 +659,117 @@
background-color: #bebebe !important;
}
.ui-listpanel li a.action { position: absolute; right: 2px; top: 2px; }
.ui-listpanel li a.action {
position: absolute;
right: 2px;
top: 2px;
}
.ui-listpanel input.search {
height: 14px;
padding: 1px;
opacity: 0.5;
margin: 4px;
width: 90%;
}
.cc-buttons-panel {
padding: 13px 20px;
}
.cc-settings-content > h1 {
font-weight: normal;
font-size: 29px;
margin-top: 0;
color: inherit;
}
.sidebar-list-panel {
}
.sidebar-list-panel.well {
padding: 0 0 17px;
}
.sidebar-list-panel__header {
font-size: 17px;
font-weight: normal;
padding-left: 14px;
}
.btn-round {
padding: 7px 7px 5px 7px;
border-radius: 30px;
line-height: 1;
}
.sidebar-list-panel__header > .btn-round {
margin-left: 5px;
padding: 3px 3px 2px 3px;
}
.accordion-light {}
.accordion-light .panel-title a span {
text-decoration: none;
border-bottom: 1px dashed;
font-weight: normal;
font-size: 16px;
}
.accordion-light .panel-title a:hover span {
color: #555;
}
.accordion-light .panel-title a i.fa {
width: 15px;
}
.accordion-light .panel-title a .fa-accordion-mark:before {
content: "\f0d7";
}
.accordion-light .panel-title a.collapsed .fa-accordion-mark:before {
content: "\f0da";
}
.accordion-light .panel-body {
border-left: 5px solid #555;
padding-left: 19px;
}
.accordion-light .panel-collapse {
padding-left: 3px;
}
.form-vertical .checkbox > label {
display: inline-block;
}
.ui-listpanel input.search { height: 14px; padding: 1px; opacity: 0.5; margin: 4px; width: 90%; }
.form-vertical label {
display: inline-block;
}
a.dynamic-link,
a.dynamic-link:link,
a.dynamic-link:active,
a.dynamic-link:visited {
border-bottom: 1px dashed;
}
.buttons-wrapper {
padding-top: 10px;
}
.buttons-wrapper .btn {
margin-right: 10px;
}
a.dynamic-link:hover {
color: #555;
}
.control-group .controls .dynamic-link {
margin-left: 12px;
}

+ 13
- 10
views/dashboard.html View File

@ -1,14 +1,17 @@
<div>
<div id="dashboard-content" class="clearfix page">
<div id="dashboard-topbar">
<div class="topbar-right">
</div>
<div class="cc-buttons-panel">
<a href="" class="js-open-cc-settings btn">
<i class="fa fa-cog icon-small"></i> Settings
</a>
</div>
<div id="dashboard-content" class="clearfix page">
<div id="dashboard-topbar">
<div class="topbar-right">
</div>
<div id="callwaiting-list" class="sidebar list_panel"></div>
<div class="dashboard-bottom-right">
<div class="content">
<div id="dashboard-view">
</div>
</div>
<div id="callwaiting-list" class="sidebar list_panel"></div>
<div class="dashboard-bottom-right">
<div class="content">
<div id="dashboard-view">
</div>
</div>
</div>


+ 47
- 0
views/settings.html View File

@ -0,0 +1,47 @@
<div class="cc-buttons-panel">
<a href="" class="js-open-cc-dashboard btn">
<i class="fa fa-cog icon-small"></i> Dashboard
</a>
</div>
<div id="cc-settings" class="container-fluid">
<div class="row-fluid">
<div class="sidebar-list-panel span2 well">
<h4 class="sidebar-list-panel__header">
Queues
<a href="" class="js-cc-create-queue btn btn-round">
<i class="fa fa-plus icon-small"></i>
</a>
</h4>
<div id="queues-list"></div>
</div>
<div id="cc-settings-content" class="cc-settings-content span10">
<div id="accordion" class="panel-group">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" >Queue settings</a>
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse">
<div class="panel-body">
<p>HTML stands for HyperText Markup Language. HTML is the main markup language for describing the structure of Web pages. <a href="https://www.tutorialrepublic.com/html-tutorial/" target="_blank">Learn more.</a></p>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseTwo">Agents</a>
</h4>
</div>
<div id="collapseTwo" class="panel-collapse collapse in">
<div class="panel-body">
<p>Bootstrap is a powerful front-end framework for faster and easier web development. It is a collection of CSS and HTML conventions. <a href="https://www.tutorialrepublic.com/twitter-bootstrap-tutorial/" target="_blank">Learn more.</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

+ 181
- 0
views/settings_queue_form.html View File

@ -0,0 +1,181 @@
{{#if data.id}}
<h1>Edit Queue</h1>
{{else}}
<h1>Create Queue</h1>
{{/if}}
<form id="queue-form" class="form-vertical">
<div class="control-group">
<label class="control-label" for="queue-name">Name</label>
<div class="controls">
<input type="text" value="{{ data.name }}" name="name" data-toggle="tooltip"
id="queue-name" title="Friendly name for this Queue">
</div>
</div>
<div id="accordion" class="accordion-light">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent=".accordion-light" href="#queue-settings" class="collapsed">
<i class="fa fa-accordion-mark"></i>
<span>Queue settings</span>
</a>
</h4>
</div>
<div id="queue-settings" class="panel-collapse collapse">
<div class="panel-body">
<div class="control-group">
<span class="control-label"></span>
<div class="controls">
<div class="checkbox">
<label>
<input type="checkbox" value="" name="record_caller" {{#if data.record_caller }}checked="checked"{{/if}} >
<span data-toggle="tooltip" title="You can also change individual agent's recording settings under the Agent's section">Call Recording</span>
</label>
</div>
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-music-on-hold">Music on Hold</label>
<div class="controls">
<select name="moh" id="queue-music-on-hold" data-toggle="tooltip" title="Select the media file that you want to be played by default when it's not set on a user or a device.">
{{#each media_list}}
{{#compare @root.data.moh "===" id}}
<option id="{{ id }}" value="{{ id }}" selected="selected">{{ name }}</option>
{{else}}
<option id="{{ id }}" value="{{ id }}">{{ name }}</option>
{{/compare}}
{{/each}}
</select>
<a href="#" class="js-edit-media dynamic-link">Edit</a>
<a href="#" class="js-create-media dynamic-link">Create</a>
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-strategy">Strategy</label>
<div class="controls">
<select id="queue-strategy" name="strategy" data-toggle="tooltip" title="The queue strategy for connecting agents to caller">
{{#compare data.strategy "===" "most_idle"}}
<option value="round_robin">Round Robin</option>
<option value="most_idle" selected="selected">Most Idle</option>
{{else}}
<option value="round_robin" selected="selected">Round Robin</option>
<option value="most_idle">Most Idle</option>
{{/compare}}
</select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-call-recording-url">Call Recording URL</label>
<div class="controls">
<input id="queue-call-recording-url" type="text" value="{{ data.call_recording_url }}" name="call_recording_url"
placeholder="http://xxx.yyy.com/call_recordings" data-toggle="tooltip"
title="URL pointing to a server that will host the recording of the calls processed by this queue.">
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-agent-wrapup-time">Call Wrap-up Time (s)</label>
<div class="controls">
<input id="queue-agent-wrapup-time" type="text" name="agent_wrapup_time" placeholder="30"
value="{{ data.agent_wrapup_time }}" data-toggle="tooltip"
title="Automatic break time between calls for the agents in this queue.">
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-max-queue-size">Max Number of Calls</label>
<div class="controls">
<input type="text" name="max_queue_size" value="{{ data.max_queue_size }}"
id="queue-max-queue-size" placeholder="10000" data-toggle="tooltip"
title="How many callers are allowed to wait on hold in the queue (0 for no limit).">
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-connection-timeout">Max Hold Time (s)</label>
<div class="controls">
<input type="text" name="connection_timeout" value="{{ data.connection_timeout }}"
id="queue-connection-timeout" placeholder="30" data-toggle="tooltip"
title="In seconds, how long to try to connect the caller before progressing past the queue callflow action (0 for no limit).">
</div>
</div>
<div class="control-group">
<span class="control-label"></span>
<div class="controls">
<div class="checkbox">
<label>
<input type="checkbox" name="enter_when_empty" {{#if data.enter_when_empty }}checked="checked"{{/if}}>
Allows a caller to enter this queue when no agents are available.
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent=".accordion-light" href="#notifications-settings" class="collapsed">
<i class="fa fa-accordion-mark"></i>
<span>Notifications settings</span>
</a>
</h4>
</div>
<div id="notifications-settings" class="panel-collapse collapse">
<div class="panel-body">
<div class="control-group">
<label class="control-label" for="queue-notifications-hangup">Notification on Hangup</label>
<div class="controls">
<input type="text" name="notifications.hangup" value="{{ data.notifications.hangup }}"
id="queue-notifications-hangup" placeholder="http://xxx.yyy/script_hangup.php" data-toggle="tooltip"
title="URL for a callback when the call ends to tell the customer on their own servers that a call has ended.">
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-notifications-pickup">Notification after Pickup</label>
<div class="controls">
<input type="text" name="notifications.pickup" value="{{ data.notifications.pickup }}"
id="queue-notifications-pickup" placeholder="http://xxx.yyy/script_pickup.php" data-toggle="tooltip"
title="URL for a callback when the call is picked up to tell the customer on their own servers that a call has been picked up.">
</div>
</div>
<div class="control-group">
<label class="control-label" for="queue-notifications-method">Method</label>
<div class="controls">
<select name="notifications.method" data-toggle="tooltip" id="queue-notifications-method"
title="What HTTP method to use">
{{#compare data.notifications.method "===" "POST"}}
<option value="GET">GET</option>
<option value="POST" selected="selected">POST</option>
{{else}}
<option value="GET" selected="selected">GET</option>
<option value="POST">POST</option>
{{/compare}}
</select>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="buttons-wrapper clearfix">
{{#if data.id}}
<button class="btn btn-primary js-save-queue">Save Changes</button>
<button class="btn btn-danger js-delete-queue">Save Changes</button>
{{else}}
<button class="btn btn-primary js-save-queue">Create</button>
{{/if}}
<button class="btn js-cancel">Cancel</button>
</div>
</form>

+ 4
- 0
views/settings_queues_list.html View File

@ -0,0 +1,4 @@
<ul class="js-nav-list nav nav-list">
<li class="active"><a href="#">Dummy Queue 1</a></li>
<li><a href="#">Dummy Queue 2</a></li>
</ul>

Loading…
Cancel
Save