You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

456 lines
14 KiB

define(function(require){
var $ = require('jquery'),
_ = require('underscore'),
monster = require('monster');
var app = {
requests: {},
subscribe: {
'voip.callLogs.render': 'callLogsRender'
},
callLogsRender: function(args) {
var self = this;
self.callLogsRenderContent(args.parent, args.fromDate, args.toDate, args.type, args.callback);
},
callLogsRenderContent: function(parent, fromDate, toDate, type, callback) {
var self = this,
template,
defaultDateRange = 1,
maxDateRange = 31;
if(!toDate && !fromDate) {
var dates = monster.util.getDefaultRangeDates(defaultDateRange);
fromDate = dates.from;
toDate = dates.to;
}
var dataTemplate = {
timezone: 'GMT' + jstz.determine_timezone().offset(),
type: type || 'today',
fromDate: fromDate,
toDate: toDate,
showFilteredDates: ['thisMonth', 'thisWeek'].indexOf(type) >= 0,
showReport: monster.config.whitelabel.callReportEmail ? true : false
};
// Reset variables used to link A-Legs & B-Legs sent by different pages in the API
delete self.lastALeg;
delete self.loneBLegs;
self.callLogsGetCdrs(fromDate, toDate, function(cdrs, nextStartKey) {
cdrs = self.callLogsFormatCdrs(cdrs);
dataTemplate.cdrs = cdrs;
template = $(monster.template(self, 'callLogs-layout', dataTemplate));
if(cdrs && cdrs.length) {
var cdrsTemplate = $(monster.template(self, 'callLogs-cdrsList', {cdrs: cdrs, showReport: monster.config.whitelabel.callReportEmail ? true : false}));
template.find('.call-logs-grid .grid-row-container')
.append(cdrsTemplate);
}
var optionsDatePicker = {
container: template,
range: maxDateRange
};
monster.ui.initRangeDatepicker(optionsDatePicker);
template.find('#startDate').datepicker('setDate', fromDate);
template.find('#endDate').datepicker('setDate', toDate);
if(!nextStartKey) {
template.find('.call-logs-loader').hide();
}
self.callLogsBindEvents({
template: template,
cdrs: cdrs,
fromDate: fromDate,
toDate: toDate,
nextStartKey: nextStartKey
});
monster.ui.tooltips(template);
parent
.empty()
.append(template);
callback && callback();
});
},
callLogsBindEvents: function(params) {
var self = this,
template = params.template,
cdrs = params.cdrs,
fromDate = params.fromDate,
toDate = params.toDate,
startKey = params.nextStartKey;
setTimeout(function() { template.find('.search-query').focus() });
template.find('.apply-filter').on('click', function(e) {
var fromDate = template.find('input.filter-from').datepicker("getDate"),
toDate = template.find('input.filter-to').datepicker("getDate");
self.callLogsRenderContent(template.parents('.right-content'), fromDate, toDate, 'custom');
});
template.find('.fixed-ranges button').on('click', function(e) {
var $this = $(this),
type = $this.data('type');
// We don't really need to do that, but it looks better to the user if we still remove/add the classes instantly.
template.find('.fixed-ranges button').removeClass('active');
$this.addClass('active');
if(type !== 'custom') {
// Without this, it doesn't look like we're refreshing the data.
// GOod way to solve it would be to separate the filters from the call logs view, and only refresh the call logs.
template.find('.call-logs-content').empty();
var dates = self.callLogsGetFixedDatesFromType(type);
self.callLogsRenderContent(template.parents('.right-content'), dates.from, dates.to, type);
}
else {
template.find('.fixed-ranges-date').hide();
template.find('.custom-range').addClass('active');
}
});
template.find('.download-csv').on('click', function(e) {
var fromDateTimestamp = monster.util.dateToBeginningOfGregorianDay(fromDate),
toDateTimestamp = monster.util.dateToEndOfGregorianDay(toDate);
window.location.href = self.apiUrl + 'accounts/' + self.accountId
+ '/cdrs?created_from=' + fromDateTimestamp + '&created_to=' + toDateTimestamp
+ '&accept=text/csv&auth_token=' + self.authToken;
});
template.find('.search-div input.search-query').on('keyup', function(e) {
if(template.find('.grid-row-container .grid-row').length > 0) {
var searchValue = $(this).val().replace(/\|/g,'').toLowerCase(),
matchedResults = false;
if(searchValue.length <= 0) {
template.find('.grid-row-group').show();
matchedResults = true;
} else {
_.each(cdrs, function(cdr) {
var callIds = (cdr.callId || cdr.id) + (cdr.bLegs.length>0 ? "|" + $.map(cdr.bLegs, function(val) { return val.callId || val.id }).join("|") : ""),
searchString = (cdr.date + "|" + cdr.fromName + "|"
+ cdr.fromNumber + "|" + cdr.toName + "|"
+ cdr.toNumber + "|" + cdr.hangupCause + "|"
+ callIds).toLowerCase(),
rowGroup = template.find('.grid-row.a-leg[data-id="'+cdr.id+'"]').parents('.grid-row-group');
if(searchString.indexOf(searchValue) >= 0) {
matchedResults = true;
rowGroup.show();
} else {
var matched = _.find(cdr.bLegs, function(bLeg) {
var searchStr = (bLeg.date + "|" + bLeg.fromName + "|"
+ bLeg.fromNumber + "|" + bLeg.toName + "|"
+ bLeg.toNumber + "|" + bLeg.hangupCause).toLowerCase();
return searchStr.indexOf(searchValue) >= 0;
});
if(matched) {
matchedResults = true;
rowGroup.show();
} else {
rowGroup.hide();
}
}
})
}
if(matchedResults) {
template.find('.grid-row.no-match').hide();
} else {
template.find('.grid-row.no-match').show();
}
}
});
template.on('click', '.a-leg.has-b-legs', function(e) {
var rowGroup = $(this).parents('.grid-row-group');
if(rowGroup.hasClass('open')) {
rowGroup.removeClass('open');
rowGroup.find('.b-leg').slideUp();
} else {
template.find('.grid-row-group').removeClass('open');
template.find('.b-leg').slideUp();
rowGroup.addClass('open');
rowGroup.find('.b-leg').slideDown();
}
});
template.on('click', '.grid-cell.details i', function(e) {
e.stopPropagation();
var cdrId = $(this).parents('.grid-row').data('id');
self.callLogsShowDetailsPopup(cdrId);
});
template.on('click', '.grid-cell.report a', function(e) {
e.stopPropagation();
});
function loadMoreCdrs() {
var loaderDiv = template.find('.call-logs-loader');
if(startKey) {
loaderDiv.toggleClass('loading');
loaderDiv.find('.loading-message > i').toggleClass('fa-spin');
self.callLogsGetCdrs(fromDate, toDate, function(newCdrs, nextStartKey) {
newCdrs = self.callLogsFormatCdrs(newCdrs);
cdrsTemplate = $(monster.template(self, 'callLogs-cdrsList', {cdrs: newCdrs, showReport: monster.config.whitelabel.callReportEmail ? true : false}));
startKey = nextStartKey;
if(!startKey) {
template.find('.call-logs-loader').hide();
}
template.find('.call-logs-grid .grid-row-container').append(cdrsTemplate);
cdrs = cdrs.concat(newCdrs);
var searchInput = template.find('.search-div input.search-query');
if(searchInput.val()) {
searchInput.keyup();
}
loaderDiv.toggleClass('loading');
loaderDiv.find('.loading-message > i').toggleClass('fa-spin');
}, startKey);
} else {
loaderDiv.hide();
}
}
template.find('.call-logs-grid').on('scroll', function(e) {
var $this = $(this);
if($this.scrollTop() === $this[0].scrollHeight - $this.innerHeight()) {
loadMoreCdrs();
}
});
template.find('.call-logs-loader:not(.loading) .loader-message').on('click', function(e) {
loadMoreCdrs();
});
},
// Function built to return JS Dates for the fixed ranges.
callLogsGetFixedDatesFromType: function(type) {
var self = this,
from = new Date(),
to = new Date();
switch(type) {
case 'today':
break;
case 'thisWeek':
// First we need to know how many days separate today and monday.
// Since Sunday is 0 and Monday is 1, we do this little substraction to get the result.
var day = from.getDay(),
countDaysFromMonday = (day||7) - 1;
from.setDate(from.getDate() - countDaysFromMonday);
break;
case 'thisMonth':
from.setDate(1);
break;
default:
break;
}
return {
from: from,
to: to
};
},
callLogsGetCdrs: function(fromDate, toDate, callback, pageStartKey) {
var self = this,
fromDateTimestamp = monster.util.dateToBeginningOfGregorianDay(fromDate),
toDateTimestamp = monster.util.dateToEndOfGregorianDay(toDate),
filters = {
'created_from': fromDateTimestamp,
'created_to': toDateTimestamp,
'page_size': 50
};
if(pageStartKey) {
filters['start_key'] = pageStartKey;
}
self.callApi({
resource: 'cdrs.list',
data: {
accountId: self.accountId,
filters: filters
},
success: function(data, status) {
var cdrs = {},
groupedLegs = _.groupBy(data.data, function(val) { return (val.direction === 'inbound' || !val.bridge_id) ? 'aLegs' : 'bLegs' });
if(self.lastALeg) {
groupedLegs.aLegs.splice(0, 0, self.lastALeg);
}
// if(self.loneBLegs && self.loneBLegs.length) {
// groupedLegs.bLegs = self.loneBLegs.concat(groupedLegs.bLegs);
// }
if(data['next_start_key']) {
self.lastALeg = groupedLegs.aLegs.pop();
}
_.each(groupedLegs.aLegs, function(val) {
var call_id = val.call_id || val.id;
cdrs[call_id] = { aLeg: val, bLegs: {} };
});
if(self.loneBLegs && self.loneBLegs.length > 0) {
_.each(self.loneBLegs, function(val) {
if('other_leg_call_id' in val && val.other_leg_call_id in cdrs) {
cdrs[val.other_leg_call_id].bLegs[val.id] = val;
}
});
}
self.loneBLegs = [];
_.each(groupedLegs.bLegs, function(val) {
if('other_leg_call_id' in val) {
if(val.other_leg_call_id in cdrs) {
cdrs[val.other_leg_call_id].bLegs[val.id] = val;
} else {
self.loneBLegs.push(val);
}
}
});
callback(cdrs, data['next_start_key']);
}
});
},
callLogsFormatCdrs: function(cdrs) {
var self = this,
result = [],
formatCdr = function(cdr) {
var date = monster.util.gregorianToDate(cdr.timestamp),
shortDate = monster.util.toFriendlyDate(date, 'shortDate'),
time = monster.util.toFriendlyDate(date, 'time'),
durationMin = parseInt(cdr.duration_seconds/60).toString(),
durationSec = (cdr.duration_seconds % 60 < 10 ? "0" : "") + (cdr.duration_seconds % 60),
hangupI18n = self.i18n.active().hangupCauses,
hangupHelp = '',
isOutboundCall = "authorizing_id" in cdr && cdr.authorizing_id.length > 0;
// Only display help if it's in the i18n.
if(hangupI18n.hasOwnProperty(cdr.hangup_cause)) {
if(isOutboundCall && hangupI18n[cdr.hangup_cause].hasOwnProperty('outbound')) {
hangupHelp += hangupI18n[cdr.hangup_cause].outbound;
}
else if(!isOutboundCall && hangupI18n[cdr.hangup_cause].hasOwnProperty('inbound')) {
hangupHelp += hangupI18n[cdr.hangup_cause].inbound;
}
}
return {
id: cdr.id,
callId: cdr.call_id,
timestamp: cdr.timestamp,
date: shortDate,
time: time,
fromName: cdr.caller_id_name,
fromNumber: cdr.caller_id_number || cdr.from.replace(/@.*/, ''),
toName: cdr.callee_id_name,
toNumber: cdr.callee_id_number || ("request" in cdr) ? cdr.request.replace(/@.*/, '') : cdr.to.replace(/@.*/, ''),
duration: durationMin + ":" + durationSec,
hangupCause: cdr.hangup_cause,
hangupHelp: hangupHelp,
isOutboundCall: isOutboundCall,
mailtoLink: "mailto:" + monster.config.whitelabel.callReportEmail
+ "?subject=Call Report: " + cdr.call_id
+ "&body=Please describe the details of the issue:%0D%0A%0D%0A"
+ "%0D%0A____________________________________________________________%0D%0A"
+ "%0D%0AAccount ID: " + self.accountId
+ "%0D%0AFrom (Name): " + (cdr.caller_id_name || "")
+ "%0D%0AFrom (Number): " + (cdr.caller_id_number || cdr.from.replace(/@.*/, ''))
+ "%0D%0ATo (Name): " + (cdr.callee_id_name || "")
+ "%0D%0ATo (Number): " + (cdr.callee_id_number || ("request" in cdr) ? cdr.request.replace(/@.*/, '') : cdr.to.replace(/@.*/, ''))
+ "%0D%0ADate: " + shortDate
+ "%0D%0ADuration: " + durationMin + ":" + durationSec
+ "%0D%0AHangup Cause: " + (cdr.hangup_cause || "")
+ "%0D%0ACall ID: " + cdr.call_id
+ "%0D%0AOther Leg Call ID: " + (cdr.other_leg_call_id || "")
+ "%0D%0AHandling Server: " + (cdr.handling_server || "")
};
};
_.each(cdrs, function(val, key) {
if(!('aLeg' in val)) {
// Handling lone b-legs as standalone a-legs
_.each(val.bLegs, function(v, k) {
result.push($.extend({ bLegs: [] }, formatCdr(v)));
});
} else {
var cdr = formatCdr(val.aLeg);
cdr.bLegs = [];
_.each(val.bLegs, function(v, k) {
cdr.bLegs.push(formatCdr(v));
});
result.push(cdr);
}
});
result.sort(function(a, b) {
return b.timestamp - a.timestamp;
})
return result;
},
callLogsShowDetailsPopup: function(callLogId) {
var self = this;
self.callApi({
resource: 'cdrs.get',
data: {
accountId: self.accountId,
cdrId: callLogId
},
success: function(data, status) {
function objToArray(obj, prefix) {
var prefix = prefix || "",
result = [];
_.each(obj, function(val, key) {
if(typeof val === "object") {
result = result.concat(objToArray(val, prefix+key+"."));
} else {
result.push({
key: prefix+key,
value: val
});
}
});
return result;
}
var detailsArray = objToArray(data.data);
detailsArray.sort(function(a, b) {
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
})
monster.ui.dialog(
monster.template(self, 'callLogs-detailsPopup', { details: detailsArray }),
{ title: self.i18n.active().callLogs.detailsPopupTitle }
);
},
error: function(data, status) {
monster.ui.alert('error', self.i18n.active().callLogs.alertMessages.getDetailsError);
}
});
}
};
return app;
});