diff --git a/README.md b/README.md index 2e60f90..7b63542 100644 --- a/README.md +++ b/README.md @@ -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` ) \ No newline at end of file diff --git a/src/apps/callflows/app.js b/src/apps/callflows/app.js index 1574125..afd6391 100644 --- a/src/apps/callflows/app.js +++ b/src/apps/callflows/app.js @@ -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); diff --git a/src/apps/callflows/i18n/en-US.json b/src/apps/callflows/i18n/en-US.json index 8bfdc83..9286432 100644 --- a/src/apps/callflows/i18n/en-US.json +++ b/src/apps/callflows/i18n/en-US.json @@ -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": { diff --git a/src/apps/callflows/submodules/callcenter/callcenter.css b/src/apps/callflows/submodules/callcenter/callcenter.css new file mode 100644 index 0000000..d075bd1 --- /dev/null +++ b/src/apps/callflows/submodules/callcenter/callcenter.css @@ -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; +} diff --git a/src/apps/callflows/submodules/callcenter/callcenter.js b/src/apps/callflows/submodules/callcenter/callcenter.js new file mode 100644 index 0000000..80bd0a9 --- /dev/null +++ b/src/apps/callflows/submodules/callcenter/callcenter.js @@ -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 = $('