From 213cb7cb8c4ad636773b18fcdc4afcbc9c79b8fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Guti=C3=A9rrez?= Date: Mon, 22 Apr 2019 12:50:20 -0600 Subject: [PATCH] UI-3342: Edit and save combo key labels for device (#135) --- i18n/de-DE.json | 9 +- i18n/en-US.json | 12 +- i18n/es-ES.json | 5 +- i18n/fr-FR.json | 5 +- i18n/ru-RU.json | 9 +- submodules/devices/devices.js | 97 +++++++--- .../devices/{devices.css => devices.scss} | 172 ++++++++++++++++-- .../devices/views/devices-sip_device.html | 167 +++++++++-------- 8 files changed, 339 insertions(+), 137 deletions(-) rename submodules/devices/{devices.css => devices.scss} (79%) diff --git a/i18n/de-DE.json b/i18n/de-DE.json index f772738..83a95ad 100644 --- a/i18n/de-DE.json +++ b/i18n/de-DE.json @@ -289,7 +289,9 @@ "__comment": "UI-1351: Möglichkeit zur Konfiguration von Funktionstasten für SIP-Geräte in SmartPBX hinzugefügt", "__version": "v3.20_s4", "keys": { - "alertMessage": "In der Standardeinstellung wird diese Taste als Leitungstaste verwendet. Wir empfehlen, dies nicht zu ändern.", + "alert": { + "message": "In der Standardeinstellung wird diese Taste als Leitungstaste verwendet. Wir empfehlen, dies nicht zu ändern." + }, "featureKeys": { "title": "Funktionstasten", "label": "Funktionstaste" @@ -299,8 +301,8 @@ "label": "Kombitaste" }, "labels": { - "user": "Benutzer", "parkingSpot": "Halteposition", + "user": "Benutzer", "value": "Wert" }, "info": { @@ -314,7 +316,8 @@ "parking": "Wird angezeigt, wenn ein Anruf eine Halteposition belegt, die für das gesamte Unternehmen zur Verfügung steht. Die Anzeige kann verwendet werden, um einen Anruf in die Halteposition zu verlegen oder um einen gehaltenen Anruf wieder abzurufen.", "personal_parking": "Wird angezeigt, wenn ein Anruf eine Halteposition belegt, die für den ausgewählten Benutzer vorgesehen ist. Die Anzeige kann verwendet werden, um einen Anruf in die Halteposition zu verlegen oder um einen gehaltenen Anruf wieder abzurufen.", "speed_dial": "Zeigt keine Anrufereignisse an, kann jedoch zum schnellen Anrufen der konfigurierten Nummer verwendet werden." - } + }, + "typeTitle": "{{variable}}: " }, "types": { "line": "Zeile", diff --git a/i18n/en-US.json b/i18n/en-US.json index d4b25a6..4124fe9 100644 --- a/i18n/en-US.json +++ b/i18n/en-US.json @@ -293,7 +293,10 @@ "__comment": "UI-1351: Add ability to configure feature keys for SIP devices in SmartPBX", "__version": "v3.20_s4", "keys": { - "alertMessage": "By default, this key will be used as a Line Key and we recommend you not to change it.", + "alert": { + "message": "By default, this key will be used as a Line Key. It is strongly recommended that you not change it.", + "title": "Key 1 Default" + }, "featureKeys": { "title": "Feature Keys", "label": "Feature key" @@ -303,8 +306,10 @@ "label": "Combo key" }, "labels": { - "user": "User", + "deviceLabel": "Label on Device", + "function": "Function", "parkingSpot": "Parking Spot", + "user": "User", "value": "Value" }, "info": { @@ -318,7 +323,8 @@ "parking": "Indicate when a call is placed on a parking slot available to the entire company. The indicator can be used to transfer a call to the parking slot or retrieve a parked call.", "personal_parking": "Indicate when a call is placed on a parking slot dedicated to the selected user. The indicator can be used to transfer a call to the parking slot or retrieve a parked call.", "speed_dial": "Does not indicate for any calls event but can be used to rapidly call the configured number." - } + }, + "typeTitle": "{{variable}}: " }, "types": { "line": "Line", diff --git a/i18n/es-ES.json b/i18n/es-ES.json index 1c118d5..afd2e17 100644 --- a/i18n/es-ES.json +++ b/i18n/es-ES.json @@ -232,8 +232,8 @@ "menuTitle": "Teclas de funciones", "description": "Tecla de funcion", "labels": { - "user": "Usuario", "parkingSpot": "El punto de estacionamiento ", + "user": "Usuario", "value": "Valor" }, "info": { @@ -246,7 +246,8 @@ "parking": "Indicar cuando una llamada a sido puesta en ranura de estacionamiento disponible a la compañía entera. El indicador puede ser usada para transferir una llamada al la ranura de estacionamiento ò contestar una llamada estacionada.", "personal_parking": "Indicar cuando una llamada este puesta en una ranura de estacionamiento dedicada al usuario seleccionado. El indicador puede ser usado para transferir una llamada a la ranura de estacionamiento or recivir una llamada ya estacionada.", "speed_dial": "No es indicada para cualquier evento de llamada pero se puede usar para llamar el número configurado rapidamente." - } + }, + "typeTitle": "{{variable}}: " }, "types": { "none": "Ningun", diff --git a/i18n/fr-FR.json b/i18n/fr-FR.json index 18a2281..efd9f5c 100644 --- a/i18n/fr-FR.json +++ b/i18n/fr-FR.json @@ -208,8 +208,8 @@ "menuTitle": "Feature Keys", "description": "Feature key", "labels": { - "user": "Utilisateur", "parkingSpot": "Place", + "user": "Utilisateur", "value": "Valeur" }, "info": { @@ -222,7 +222,8 @@ "parking": "Indique lorsqu'un appel est placé sur une place de parking accessible par l'intégralité de l'entreprise. L'indicateur peut être utilisé pour transférer un appel vers la place de parking ou récupérer un appel en attente.", "personal_parking": "Indique lorsqu'un appel est placé sur une place de parking dédié à l'utilisateur sélectionné. L'indicateur peut être utilisé pour transférer un appel vers la place de parking ou récupérer un appel en attente.", "speed_dial": "N'indique pas l'état d'un appel mais peut être utilisé pour appeller rapidement le numéro configuré." - } + }, + "typeTitle": "{{variable}}: " }, "types": { "none": "Aucun", diff --git a/i18n/ru-RU.json b/i18n/ru-RU.json index c23a7b6..f847fdb 100644 --- a/i18n/ru-RU.json +++ b/i18n/ru-RU.json @@ -203,7 +203,9 @@ "unselectedCodecs": "Доступные кодеки" }, "keys": { - "alertMessage": "По-умолчанию эта клавиша будет использована как клавиша линии и мы рекомендуем Вам не изменять этого.", + "alert": { + "message": "По-умолчанию эта клавиша будет использована как клавиша линии и мы рекомендуем Вам не изменять этого." + }, "featureKeys": { "title": "Функциональные клавиши", "label": "Функциональная клавиша" @@ -213,8 +215,8 @@ "label": "Комбинированная клавиша" }, "labels": { - "user": "Пользователь", "parkingSpot": "Парковочная орбита", + "user": "Пользователь", "value": "Значение" }, "info": { @@ -228,7 +230,8 @@ "parking": "Показывать, когда звонок поставлен на парковочную орбиту, доступную всем пользователям. Индикатор может быть использован для парковки звонка или подключения к запаркованному звонку.", "personal_parking": "Показывать, когда звонок поставлен на персональную парковочную орбиту пользователя. Индикатор может быть использован для парковки звонка или подключения к запаркованному звонку.", "speed_dial": "Не отображать события звонка, использовать только для быстрого набора." - } + }, + "typeTitle": "{{variable}}: " }, "types": { "line": "Линия", diff --git a/submodules/devices/devices.js b/submodules/devices/devices.js index 096a9b0..cf2304a 100644 --- a/submodules/devices/devices.js +++ b/submodules/devices/devices.js @@ -212,17 +212,13 @@ define(function(require) { }); var actions = [ 'none', 'presence', 'parking', 'personal_parking', 'speed_dial' ], - parkingSpots = [], + parkingSpots = _.range(1, 11), // Array with integer numbers >= 1 and < 11 extra; users.sort(function(a, b) { return a.last_name.toLowerCase() > b.last_name.toLowerCase() ? 1 : -1; }); - for (var i = 0; i < 10; i++) { - parkingSpots[i] = i + 1; - } - _.each(actions, function(action, idx, list) { list[idx] = { id: action, @@ -244,18 +240,28 @@ define(function(require) { }; _.each(keyTypes, function(key) { - var camelCaseKey = self.devicesSnakeToCamel(key); + var camelCaseKey = _.camelCase(key); extra.provision.keys.push({ id: key, type: camelCaseKey, title: self.i18n.active().devices.popupSettings.keys[camelCaseKey].title, label: self.i18n.active().devices.popupSettings.keys[camelCaseKey].label, - data: dataDevice.provision[key] + data: _.map(dataDevice.provision[key], function(dataItem) { + var value = _.get(dataItem, 'value', {}); + + if (_.isString(value)) { + dataItem.value = { + value: value + }; + } + + return dataItem; + }) }); }); - dataDevice.extra = dataDevice.hasOwnProperty('extra') ? $.extend(true, {}, dataDevice.extra, extra) : extra; + dataDevice.extra = _.has(dataDevice, 'extra') ? _.merge({}, dataDevice.extra, extra) : extra; self.devicesRenderDevice(dataDevice, callbackSave, callbackDelete); } @@ -351,25 +357,39 @@ define(function(require) { _.each(value.data, function(val, key) { if (val) { var groupSelector = '.control-group[data-id="' + key + '"] ', - valueSelector = '.feature-key-value[data-type="' + val.type + '"]'; + valueSelector = '.feature-key-value[data-type~="' + val.type + '"]'; templateDevice .find(section.concat(groupSelector, valueSelector)) - .addClass('active') - .find('[name="provision.keys.' + value.id + '[' + key + '].value"]') - .val(val.value); + .addClass('active'); + templateDevice.find(valueSelector + ' [name="provision.keys.' + value.id + '[' + key + '].value.value"]') + .val(_.get(val, 'value.value')); + templateDevice.find(valueSelector + ' [name="provision.keys.' + value.id + '[' + key + '].value.label"]') + .val(_.get(val, 'value.label')); } }); }); templateDevice.find('.keys').sortable({ items: '.control-group', + placeholder: 'control-group placeholder', update: function() { - templateDevice - .find('.feature-key-index') - .each(function(idx, el) { - $(el).text(idx + 1); - }); + var $this = $(this); + + $this + .find('.feature-key-index') + .each(function(idx, el) { + $(el).text(idx + 1); + }); + + if ($this.data('section') === 'comboKeys') { + $this + .find('.control-group') + .first() + .addClass('warning') + .siblings('.control-group.warning') + .removeClass('warning'); + } } }); @@ -610,10 +630,12 @@ define(function(require) { }); templateDevice.find('.feature-key-type').on('change', function() { - var type = $(this).val(); + var $this = $(this), + type = $this.val(), + $featureKeyValue = $this.closest('.feature-key-value'); - $(this).siblings('.feature-key-value.active').removeClass('active'); - $(this).siblings('.feature-key-value[data-type="' + type + '"]').addClass('active'); + $featureKeyValue.siblings('.feature-key-value.active[data-type]').removeClass('active'); + $featureKeyValue.siblings('.feature-key-value[data-type~="' + type + '"]').addClass('active'); }); templateDevice.find('.tabs-section[data-section="featureKeys"] .type-info a').on('click', function() { @@ -668,7 +690,15 @@ define(function(require) { hasCodecs = $.inArray(originalData.device_type, ['sip_device', 'landline', 'fax', 'ata', 'softphone', 'smartphone', 'mobile', 'sip_uri']) > -1, hasCallForward = $.inArray(originalData.device_type, ['landline', 'cellphone', 'smartphone']) > -1, hasRTP = $.inArray(originalData.device_type, ['sip_device', 'mobile', 'softphone']) > -1, - formData = monster.ui.getFormData('form_device'); + formData = monster.ui.getFormData('form_device'), + isValuePropertyEmpty = function(data, property) { + return _ + .chain(data) + .get(['value', property]) + .trim() + .isEmpty() + .value(); + }; if ('mac_address' in formData) { formData.mac_address = monster.util.formatMacAddress(formData.mac_address); @@ -711,7 +741,7 @@ define(function(require) { }); } - if (formData.hasOwnProperty('provision') && formData.provision.hasOwnProperty('keys')) { + if (_.has(formData, 'provision.keys')) { /** * form2object sends keys back as arrays even if the first key is 1 * they needs to be coerced into an object to match the datatype in originalData @@ -719,8 +749,23 @@ define(function(require) { _.each(formData.provision.keys, function(value, key, list) { var keys = {}; - list[key].forEach(function(val, idx) { - keys[idx] = val.type === 'none' ? null : val; + _.each(list[key], function(val, idx) { + if (val.type === 'none') { + keys[idx] = null; + } else { + if (val.type === 'parking') { + val.value.value = _.parseInt(val.value.value, 10); + } + + if (key !== 'combo_keys' || isValuePropertyEmpty(val, 'label')) { + if (isValuePropertyEmpty(val, 'value')) { + delete val.value; + } else { + val.value = val.value.value; + } + } + keys[idx] = val; + } }); if (_.isEmpty(keys)) { @@ -1044,10 +1089,6 @@ define(function(require) { return formattedData; }, - devicesSnakeToCamel: function(string) { - return string.replace(/(_\w)/g, function(match) { return match[1].toUpperCase(); }); - }, - /* Utils */ devicesDeleteDevice: function(deviceId, callback) { var self = this; diff --git a/submodules/devices/devices.css b/submodules/devices/devices.scss similarity index 79% rename from submodules/devices/devices.css rename to submodules/devices/devices.scss index 9fe5a8a..9f39b83 100644 --- a/submodules/devices/devices.css +++ b/submodules/devices/devices.scss @@ -1,3 +1,5 @@ +@import "../../../../css/partials/base"; + #devices_container .devices-header { font-size: 18px; line-height: 30px; @@ -247,7 +249,6 @@ } .voip-edit-device-popup .edit-device .title-bar .device-title > div { - display: inline-block; float: left; font-size: 18px; font-weight: bold; @@ -302,12 +303,6 @@ margin: 0 40px 25px 40px; } -.voip-edit-device-popup .edit-device .content .tabs-section .helper i { - position: absolute; - top: 0; - left: -25px; -} - .voip-edit-device-popup .edit-device .content .tabs-section .number-address { display: none; } @@ -372,18 +367,157 @@ color: #aaa; } -.voip-edit-device-popup .edit-device .feature-key-value { - display: none; - margin-left: 20px; -} - -.voip-edit-device-popup .edit-device .feature-key-value.active { - display: inline-block; -} - -.voip-edit-device-popup .edit-device .feature-key-value label { - display: inline-block; - margin-right: 10px; +.voip-edit-device-popup .edit-device { + .tabs-section { + .section-header { + display: flex; + flex-direction: row; + align-items: stretch; + width: 100%; + margin-bottom: 1.5rem; + + .title, .helper { + margin: 0; + } + + .helper { + flex: 1; + position: static; + padding-left: 1rem; + text-align: right; + + .text { + padding-right: 0.5rem; + } + + .collapse { + text-align: justify; + } + } + } + + .control-group { + background-color: $white; + + &.placeholder { + background-color: $white-lilac !important; + height: 91px; + } + + .drag-handle-icon { + margin-right: 12px; + } + + .feature-key-value { + display: none; + margin-left: 20px; + + &.active { + display: inline-block; + } + + label { + display: inline-block; + margin-right: 10px; + } + } + } + + &[data-section="comboKeys"]{ + .control-group { + margin-bottom: 0; + padding: 1rem; + box-shadow: inset 0px 1px 0px 0px #DBDBDE, + inset 0px -1px 0px 0px #DBDBDE; + + &.warning { + background-color: #FCF8E3; + + .monster-panel-text { + display: block; + } + } + + &:hover { + box-shadow: inset 0px 1px 0px 0px #DBDBDE, + inset 0px -1px 0px 0px #DBDBDE, + 0px 1px 4px 0px rgba(21, 21, 23, 0.24); + + .drag-handle-icon { + width: 1rem; + margin-right: 0.75rem; + } + } + + .drag-handle-icon { + display: inline-block; + width: 0; + margin-right: 0; + transition: all 0.25s ease-in-out; + } + + .controls { + margin-left: 0px; + display: flex; + + .feature-key-value { + display:none; + margin-left: 0.5rem; + + &:first-of-type { + margin-left: 0; + } + + &.active { + display: block; + width: calc(33.33% - 0.33rem); + } + } + } + + .monster-panel-text { + display: none; + } + + label { + display: block; + float: none; + + &.control-label { + margin-bottom: 1rem; + padding-top: 0; + width: auto; + text-align: left; + font-size: 1rem; + color: $woodsmoke; + } + } + + select { + display: block; + width: 100%; + } + + input { + display: block; + width: auto; + } + + .chosen-container { + display: block; + width: 100% !important; + + .chosen-drop { + width: 100%; + + .chosen-search input { + width: 100%; + } + } + } + } + } + } } .voip-edit-device-popup .keys .fa { diff --git a/submodules/devices/views/devices-sip_device.html b/submodules/devices/views/devices-sip_device.html index 19af033..5ccd3ae 100644 --- a/submodules/devices/views/devices-sip_device.html +++ b/submodules/devices/views/devices-sip_device.html @@ -321,92 +321,105 @@ {{#each extra.provision.keys}}
-
- {{{title}}} -
-
- - - - {{ @root.i18n.devices.popupSettings.keys.info.link.showInfo }} - - -
- {{#each @root.extra.provision.keyActions}} - {{#if info}} -

- - {{text}} - - : {{info}} -

- {{/if}} - {{/each}} - {{#compare id '===' 'combo_keys'}} -

- - {{@root.i18n.devices.popupSettings.keys.types.line}} - - : {{@root.i18n.devices.popupSettings.keys.info.types.line}} -

- {{/compare}} +
+
+ {{{title}}} +
+
+ + + {{ @root.i18n.devices.popupSettings.keys.info.link.showInfo }} + + {{telicon 'question--circle'}} + +
+ {{#each @root.extra.provision.keyActions}} + {{#if info}} +

+ + {{replaceVar @root.i18n.devices.popupSettings.keys.info.typeTitle text}} + + {{info}} +

+ {{/if}} + {{/each}} + {{#compare id '===' 'combo_keys'}} +

+ + {{replaceVar @root.i18n.devices.popupSettings.keys.info.typeTitle @root.i18n.devices.popupSettings.keys.types.line}} + + {{@root.i18n.devices.popupSettings.keys.info.types.line}} +

+ {{/compare}} +
+
{{#each data}} -
- {{#compare ../id '===' 'combo_keys'}} - {{#compare @key '===' '0'}} -
- {{@root.i18n.devices.popupSettings.keys.alertMessage}} -
+
+ + {{#compare ../id '===' 'combo_keys'}} + {{#monsterPanelText @root.i18n.devices.popupSettings.keys.alert.title 'warning' 'fill-width'}} + {{@root.i18n.devices.popupSettings.keys.alert.message}} + {{/monsterPanelText}} {{/compare}} - {{/compare}} - -
- -
- - -
-
- - -
-
- - -
-
- - + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {{#compare ../id '===' 'combo_keys'}} +
+ + +
+ {{/compare}}
-
{{/each}} +
{{/each}}