Browse Source

UI-1161: Reset incoming call strategy (#94)

* Add reset button for incoming call handling strategy

* Delete menus and callflows for call strategy

* Fix code mistakes

* Re-create default call strategy after delete

* Add missing self variable on delete functions

* Small fixes

* Refactor code at strategyGetMainCallflows

* Refactor to use existing callflow functions

* Extract create menu API call to separate function
4.3
Guillermo Gutiérrez 7 years ago
committed by GitHub
parent
commit
8e8eb8c289
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 450 additions and 154 deletions
  1. +9
    -3
      i18n/en-US.json
  2. +439
    -150
      submodules/strategy/strategy.js
  3. +2
    -1
      submodules/strategy/views/strategy-calls.html

+ 9
- 3
i18n/en-US.json View File

@ -940,7 +940,8 @@
},
"confirmMessages": {
"deleteHoliday": "This holiday will be permanently deleted. Continue?",
"disableHolidays": "All existing holidays will be permanently deleted. Continue?"
"disableHolidays": "All existing holidays will be permanently deleted. Continue?",
"resetCalls": "All incoming call handling strategies will be reset. Continue?"
},
"alertMessages": {
"uniqueHoliday": "Every holiday must have a unique name.",
@ -952,8 +953,9 @@
"lastE911Error": "You need e911 set up on at least one of your Main Numbers. Please set up e911 on another Main Number before deleting this one."
},
"toastrMessages": {
"updateHolidaySuccess": "Holidays sucessfully updated.",
"updateCallSuccess": "Call strategy sucessfully updated.",
"updateHolidaySuccess": "Holidays successfully updated.",
"updateCallSuccess": "Call strategy successfully updated.",
"resetCallSuccess": "Call strategy successfully reset.",
"buyNumbersSuccess": "The numbers have been purchased.",
"buyNumbersError": "An error occured while purchasing numbers.",
"removeNumberSuccess": "Number successfully removed from the main callflow."
@ -1034,6 +1036,10 @@
"headerNoE911": "You've disabled E911 on the number that was used for your Emergency Caller-ID, please select a new number to use as this Account Emergency Caller-ID.",
"success": "You successfully updated the E911 on the account to be {{ number }}.",
"current": "Current one"
},
"buttons": {
"reset": "Reset"
}
},


+ 439
- 150
submodules/strategy/strategy.js View File

@ -2378,6 +2378,42 @@ define(function(require) {
});
}
});
container.on('click', '.reset-button', function(e) {
e.preventDefault();
monster.ui.confirm(self.i18n.active().strategy.confirmMessages.resetCalls, function() {
monster.waterfall([
function(callback) {
self.strategyDeleteCalls({
success: function() {
callback(null);
},
error: function() {
callback(true);
}
});
},
function(callback) {
self.strategyGetMainCallflows(function(callflows) {
strategyData.callflows = callflows;
callback(null, callflows);
});
}
], function(err, result) {
if (err) {
return;
}
container.hide();
container.parents('.element-container').removeClass('open');
monster.ui.toast({
type: 'success',
message: self.i18n.active().strategy.toastrMessages.resetCallSuccess
});
});
});
});
},
strategyRenderHolidayLine: function(container, holidayType, holiday) {
@ -2939,50 +2975,55 @@ define(function(require) {
});
},
strategyGetMainCallflows: function(callback) {
strategyGetMainCallflows: function(mainCallback) {
var self = this;
self.callApi({
resource: 'callflow.list',
data: {
accountId: self.accountId,
filters: {
'has_value': 'type',
'key_missing': ['owner_id', 'group_id']
}
monster.waterfall([
function(waterfallCallback) {
self.strategyListCallflows({
filters: {
'has_value': 'type',
'key_missing': ['owner_id', 'group_id']
},
success: function(data) {
waterfallCallback(null, data);
},
error: function() {
waterfallCallback(true);
}
});
},
success: function(data, status) {
function(data, waterfallCallback) {
var parallelRequests = {},
menuRequests = {};
_.each(data.data, function(val, key) {
if (val.type === 'main' || val.type === 'conference' || val.type === 'faxing') {
var name = val.name || val.numbers[0];
if (val.type === 'conference') {
name = 'MainConference';
} else if (val.type === 'faxing') {
name = 'MainFaxing';
}
parallelRequests[name] = function(callback) {
self.callApi({
resource: 'callflow.get',
data: {
accountId: self.accountId,
callflowId: val.id
},
success: function(data, status) {
callback(null, data.data);
}
});
};
_.each(data, function(val, key) {
if (!_.includes(['main', 'conference', 'faxing'], val.type)) {
return;
}
var name = val.name || val.numbers[0];
if (val.type === 'conference') {
name = 'MainConference';
} else if (val.type === 'faxing') {
name = 'MainFaxing';
}
parallelRequests[name] = function(callback) {
self.strategyGetCallflow({
data: {
id: val.id
},
success: function(data) {
callback(null, data);
}
});
};
});
if (!parallelRequests.MainConference) {
parallelRequests.MainConference = function(callback) {
self.callApi({
resource: 'callflow.create',
self.strategyCreateCallflow({
data: {
accountId: self.accountId,
data: {
contact_list: {
exclude: false
@ -2997,8 +3038,8 @@ define(function(require) {
}
}
},
success: function(data, status) {
callback(null, data.data);
success: function(data) {
callback(null, data);
}
});
};
@ -3006,10 +3047,8 @@ define(function(require) {
if (!parallelRequests.MainFaxing) {
parallelRequests.MainFaxing = function(callback) {
self.callApi({
resource: 'callflow.create',
self.strategyCreateCallflow({
data: {
accountId: self.accountId,
data: {
contact_list: {
exclude: false
@ -3024,8 +3063,8 @@ define(function(require) {
}
}
},
success: function(data, status) {
callback(null, data.data);
success: function(data) {
callback(null, data);
}
});
};
@ -3034,139 +3073,159 @@ define(function(require) {
_.each(self.subCallflowsLabel, function(val) {
var menuName = val + 'Menu';
if (!parallelRequests[menuName]) {
menuRequests[menuName] = function(callback) {
self.callApi({
resource: 'menu.create',
data: {
accountId: self.accountId,
data: {
name: menuName,
record_pin: monster.util.randomString(4, '1234567890'),
media: {
exit_media: true,
invalid_media: true,
transfer_media: true
},
retries: 3,
max_extension_length: 4,
type: 'main'
}
},
success: function(menuData, status) {
self.callApi({
resource: 'callflow.create',
data: {
accountId: self.accountId,
data: {
contact_list: {
exclude: false
},
numbers: [menuName],
type: 'main',
flow: {
children: {},
data: {
id: menuData.data.id
},
module: 'menu'
}
}
},
success: function(data, status) {
callback && callback(null, data.data);
}
});
}
});
};
} else if (!parallelRequests[val]) {
if (parallelRequests[menuName]) {
if (parallelRequests[val]) {
return;
}
menuRequests[menuName] = parallelRequests[menuName];
delete parallelRequests[menuName];
return;
}
});
monster.parallel(menuRequests, function(err, results) {
var mainCallflows = results;
_.each(self.subCallflowsLabel, function(val) {
if (!parallelRequests[val]) {
parallelRequests[val] = function(callback) {
self.callApi({
resource: 'callflow.create',
menuRequests[menuName] = function(callback) {
monster.waterfall([
function(innerCallback) {
self.strategyCreateMenu({
data: {
data: {
name: menuName,
record_pin: monster.util.randomString(4, '1234567890'),
media: {
exit_media: true,
invalid_media: true,
transfer_media: true
},
retries: 3,
max_extension_length: 4,
type: 'main'
}
},
success: function(menuData) {
innerCallback(null, menuData);
},
error: function(parsedError) {
innerCallback(true);
}
});
},
function(menuData, innerCallback) {
self.strategyCreateCallflow({
data: {
accountId: self.accountId,
data: {
contact_list: {
exclude: false
},
numbers: [val],
numbers: [menuName],
type: 'main',
flow: {
children: {},
data: {
id: mainCallflows[val + 'Menu'].id
id: menuData.id
},
module: 'callflow'
module: 'menu'
}
}
},
success: function(data, status) {
callback(null, data.data);
success: function(data) {
innerCallback(null, data);
},
error: function(parsedError) {
innerCallback(true);
}
});
};
}
], function(err, result) {
!err && callback && callback(null, result);
});
};
});
monster.parallel(menuRequests, function(err, mainCallflows) {
_.each(self.subCallflowsLabel, function(val) {
if (parallelRequests[val]) {
return;
}
});
monster.parallel(parallelRequests, function(err, results) {
if (!parallelRequests.MainCallflow) {
self.callApi({
resource: 'callflow.create',
parallelRequests[val] = function(callback) {
self.strategyCreateCallflow({
data: {
accountId: self.accountId,
data: {
contact_list: {
exclude: false
},
numbers: ['undefinedMainNumber'],
name: 'MainCallflow',
numbers: [val],
type: 'main',
flow: {
children: {
'_': {
children: {},
data: {
id: results.MainOpenHours.id
},
module: 'callflow'
}
children: {},
data: {
id: mainCallflows[val + 'Menu'].id
},
data: {},
module: 'temporal_route'
module: 'callflow'
}
}
},
success: function(data, status) {
results.MainCallflow = data.data;
callback($.extend(true, mainCallflows, results));
success: function(data) {
callback(null, data);
}
});
} else {
};
});
monster.parallel(parallelRequests, function(err, results) {
if (parallelRequests.MainCallflow) {
// For users who had undesired callflow with only "0" in it, we migrate it to our new empty main callflow "undefinedMainNumber"
if (results.MainCallflow.numbers && results.MainCallflow.numbers.length === 1 && results.MainCallflow.numbers[0] === '0') {
results.MainCallflow.numbers[0] = 'undefinedMainNumber';
self.strategyUpdateCallflow(results.MainCallflow, function(updatedCallflow) {
results.MainCallflow = updatedCallflow;
callback($.extend(true, mainCallflows, results));
waterfallCallback(null, $.extend(true, mainCallflows, results));
});
} else {
callback($.extend(true, mainCallflows, results));
return;
}
waterfallCallback(null, $.extend(true, mainCallflows, results));
return;
}
self.strategyCreateCallflow({
data: {
data: {
contact_list: {
exclude: false
},
numbers: ['undefinedMainNumber'],
name: 'MainCallflow',
type: 'main',
flow: {
children: {
'_': {
children: {},
data: {
id: results.MainOpenHours.id
},
module: 'callflow'
}
},
data: {},
module: 'temporal_route'
}
}
},
success: function(data) {
results.MainCallflow = data;
waterfallCallback(null, $.extend(true, mainCallflows, results));
}
});
});
});
}
], function(err, result) {
if (err) {
return;
}
mainCallback(result);
});
},
@ -3205,8 +3264,13 @@ define(function(require) {
}
listRequests.push(function(localCallback) {
self.strategyCreateCallflow(callflow, function(data) {
localCallback && localCallback(null, data);
self.strategyCreateCallflow({
data: {
data: callflow
},
success: function(data) {
localCallback && localCallback(null, data);
}
});
});
}
@ -3223,15 +3287,17 @@ define(function(require) {
},
strategyGetFeatureCodes: function(callback) {
var self = this,
filters = {
var self = this;
self.strategyListCallflows({
filters: {
paginate: 'false',
has_key: 'featurecode'
};
self.strategyGetCallflows(function(listFeatureCodes) {
callback && callback(listFeatureCodes);
}, filters);
},
success: function(listFeatureCodes) {
callback && callback(listFeatureCodes);
}
});
},
strategyGetAllRules: function(globalCallback) {
@ -3404,10 +3470,13 @@ define(function(require) {
monster.parallel(
{
callQueues: function(_callback) {
self.strategyGetCallflows(function(callQueuesData) {
_callback(null, callQueuesData);
}, {
'filter_flow.module': 'qubicle'
self.strategyListCallflows({
filters: {
'filter_flow.module': 'qubicle'
},
success: function(callQueuesData) {
_callback(null, callQueuesData);
}
});
},
users: function(_callback) {
@ -3505,10 +3574,13 @@ define(function(require) {
});
},
advancedCallflows: function(_callback) {
self.strategyGetCallflows(function(advancedCallflowsData) {
_callback(null, advancedCallflowsData);
}, {
'filter_ui_is_main_number_cf': true
self.strategyListCallflows({
filters: {
'filter_ui_is_main_number_cf': true
},
success: function(advancedCallflowsData) {
_callback(null, advancedCallflowsData);
}
});
}
},
@ -3664,32 +3736,35 @@ define(function(require) {
mainCallflow.flow.data.rules = ruleArray;
},
strategyGetCallflows: function(callback, filters) {
strategyListCallflows: function(args) {
var self = this;
self.callApi({
resource: 'callflow.list',
data: {
accountId: self.accountId,
filters: filters || {}
filters: args.filters || {}
},
success: function(callflowData) {
callback && callback(callflowData.data);
args.hasOwnProperty('success') && args.success(callflowData.data);
},
error: function(parsedError) {
args.hasOwnProperty('error') && args.error(parsedError);
}
});
},
strategyCreateCallflow: function(callflow, callback) {
strategyCreateCallflow: function(args) {
var self = this;
self.callApi({
resource: 'callflow.create',
data: {
accountId: self.accountId,
data: callflow
data: args.data.data
},
success: function(callflowData) {
callback && callback(callflowData.data);
args.hasOwnProperty('success') && args.success(callflowData.data);
}
});
},
@ -3864,6 +3939,220 @@ define(function(require) {
args.hasOwnProperty('error') && args.error();
}
});
},
strategyGetCallflow: function(args) {
var self = this;
self.callApi({
resource: 'callflow.get',
data: {
accountId: self.accountId,
callflowId: args.data.id
},
success: function(data, status) {
args.hasOwnProperty('success') && args.success(data.data);
},
error: function(parsedError) {
args.hasOwnProperty('error') && args.error(parsedError);
}
});
},
strategyDeleteCalls: function(args) {
var self = this;
monster.waterfall([
// Get main callflows created via SmartPBX
function(callback) {
self.strategyListCallflows({
filters: {
'paginate': false,
'has_value': 'type',
'filter_type': 'main',
'key_missing': [
'owner_id',
'group_id'
],
'filter_ui_metadata.origin': [
'voip'
]
},
success: function(data) {
// Convert callflows array to map object, then send to next step
callback(null,
_.reduce(data, function(obj, callflow) {
var label = callflow.name || callflow.numbers[0];
obj[label] = callflow;
return obj;
}, {}));
},
error: function(parsedError) {
callback(parsedError);
}
});
},
// Delete menus and callflows
function(mainCallflows, callback) {
monster.parallel(
_.reduce(self.subCallflowsLabel, function(parallelCalls, label) {
var deleteSequence = self.strategyCreateSingleCallStrategyDeleteSequence(label, mainCallflows);
if (_.isEmpty(deleteSequence)) {
return parallelCalls;
}
parallelCalls.push(function(callback) {
monster.waterfall(deleteSequence, function(err, results) {
callback(null);
});
});
return parallelCalls;
}, []),
function(err, results) {
if (err) {
callback(err);
return;
}
callback(null);
});
}
], function(err, results) {
if (err) {
args.hasOwnProperty('error') && args.error(err);
return;
}
args.hasOwnProperty('success') && args.success(results);
});
},
strategyCreateSingleCallStrategyDeleteSequence: function(label, mainCallflows) {
var self = this,
deleteSequence = [],
strategyCallflow = mainCallflows[label],
menuCallflow = mainCallflows[label + 'Menu'];
if (strategyCallflow) {
// Add function to delete call strategy callflow
deleteSequence.push(function(callback) {
self.strategyDeleteCallflow({
data: {
id: strategyCallflow.id
},
success: function() {
callback(null);
},
error: function() {
callback(true);
}
});
});
}
if (!menuCallflow) {
return deleteSequence;
}
// There is a menu callflow, so create functions to...
// ...get callflow details (to get menu ID),...
deleteSequence.push(function(callback) {
self.strategyGetCallflow({
data: {
id: menuCallflow.id
},
success: function(menuCallflowDetails) {
callback(null, menuCallflowDetails);
},
error: function() {
callback(true);
}
});
});
// ...then delete menu callflow
deleteSequence.push(function(menuCallflowDetails, callback) {
self.strategyDeleteCallflow({
data: {
id: menuCallflow.id
},
success: function() {
callback(null, menuCallflowDetails);
},
error: function() {
callback(true);
}
});
});
// ...and finally delete menu
deleteSequence.push(function(menuCallflowDetails, callback) {
self.strategyDeleteMenu({
data: {
id: menuCallflowDetails.flow.data.id
},
success: function() {
callback(null);
},
error: function() {
callback(true);
}
});
});
return deleteSequence;
},
strategyCreateMenu: function(args) {
var self = this;
self.callApi({
resource: 'menu.create',
data: {
accountId: self.accountId,
data: args.data.data
},
success: function(data, status) {
args.hasOwnProperty('success') && args.success(data.data);
},
error: function(parsedError) {
args.hasOwnProperty('error') && args.error(parsedError);
}
});
},
strategyDeleteMenu: function(args) {
var self = this;
self.callApi({
resource: 'menu.delete',
data: {
accountId: self.accountId,
menuId: args.data.id
},
success: function(data, status) {
args.hasOwnProperty('success') && args.success(data.data);
},
error: function(parsedError) {
args.hasOwnProperty('error') && args.error(parsedError);
}
});
},
strategyDeleteCallflow: function(args) {
var self = this;
self.callApi({
resource: 'callflow.delete',
data: {
accountId: self.accountId,
callflowId: args.data.id
},
success: function(data, status) {
args.hasOwnProperty('success') && args.success(data.data);
},
error: function(parsedError) {
args.hasOwnProperty('error') && args.error(parsedError);
}
});
}
};


+ 2
- 1
submodules/strategy/views/strategy-calls.html View File

@ -32,6 +32,7 @@
</div>
<div class="calls-actions basic-actions">
<button type="button" class="monster-button monster-button-danger pull-left reset-button">{{i18n.strategy.buttons.reset}}</button>
<a href="#" class="monster-link blue cancel-link">{{i18n.cancel}}</a>
<button class="monster-button monster-button-success save-button">{{i18n.saveChanges}}</button>
</div>
</div>

Loading…
Cancel
Save