var AdminReporting = function(core, target, data, returnCB){
function Template(){
var fromDate = 'from:
',
toDate = 'to:',
downloadReport = 'Full Platform Report:' + fromDate + toDate + '
';
this.index = '';
this.notifications = '';
this.general = '';
this.exchange = '';
this.platformAlert = '';
this.health = '';
this.search = '';
this.totalSquare = '{{ mainValue }}
{{ subTitle }}:
{{ subValue }}
';
this.notificationEvent = '- {{ type }}{{# ip }} ({{ . }}){{/ ip }}: {{# msg }} {{ . }}{{/ msg }}{{# newBudget }} budget ${{ oldBudget }} is now ${{ . }} {{/ newBudget}}{{# agency }}{{.}}, {{/ agency }}{{ advertiser }}{{# user }} ({{ . }}){{/ user }}{{# name }}, {{ . }}{{/ name }}{{ ts }}
';
this.searchResult = '{{#items}}- {{type}}: {{#campaignID}} {{campaignName}} ({{campaignID}}) | Advertiser:{{/campaignID}}{{#advertiserID}} {{advertiserName}} ({{advertiserID}}) | Agency:{{/advertiserID}} {{#agencyID}} {{agencyName}} ({{agencyID}}){{/agencyID}}.
{{/items}}{{^items}}no matches found :(
{{/items}}';
this.apps = {};
this.apps.tradeShow = {
index: 'Tradeshow
Save!
',
row: ''
};
}
var events = null,
template = null,
element = null,
elements = null,
data = null,
squares = null,
notificationsData = null,
exData = {},
health,
liveNotifications = new AdminNotifications(100),
geo = new MeteoraMap.Geocoder();
this.exit = exit;
var req = {
'advStats': { 'source' : 'actionStats/period/advertisers/1/-/-/7', 'noID' : true },
'adminReporting': null,
'cachedExchangeData': { args: 30 },
'adminMessage': null,
'health': null,
'system/apps/list': null,
};
new ApiManager(req, null, init);
function init(err, d){
if (!!err) console.error(err);
d = cleanRTBObject(!!core ? core.userID : null, d);
health = d.health;
notificationsData = d.notifications;
if(!!d.cachedExchangeData) {
for(var k in d.cachedExchangeData) {
var v = d.cachedExchangeData[k];
if(!v || Object.keys(v).length === 0) continue;
exData[k] = new ExchangeObject(d.cachedExchangeData[k]);
exData[k].name = k[0].toUpperCase() + k.substr(1);
}
} else {
setTimeout(function() {
return core.notifications.setError('I’m sorry, but exchange data is currently not available.');
}, 10);
}
events = new EventManager;
template = new Template();
element = getElementFromString(template.index),
elements = new Elements(element, {
'tabbed' : {},
'tabbedWindow' : {},
'notifications': {},
'general' : {},
'impressions' : {},
'clicks' : {},
'conversions' : {},
'graphContainer' : {},
'toggle' : {},
});
data = new Data(d.adminReporting);
squares = new Squares(d.adminReporting, d.advStats);
if(returnCB) returnCB();
initIndex(d);
if (target) target.appendChild(element);
}
function initIndex(d){
var e = elements;
//e.index = getElementFromString(template.index);
e.tabbed = element.querySelector('.tabbed');
e.notifications = getElementFromString(template.notifications);
e.general = getElementFromString(template.general);
e.exchange = getElementFromString(template.exchange);
e.platformAlert = getElementFromString(render(template.platformAlert, d.adminMessage));
e.health = getElementFromString(template.exchange, d.health);
e.search = getElementFromString(template.search);
if(isDefined(data.graph)) initGraph(e);
initTabbedWindow();
}
function initTabbedWindow(){
var e = elements;
elements.tabbedWindow = new TabbedContainer(e.tabbed, getTabbedObj(e));
if(data.graph) elements.toggle = new TabbedContainer(e.graphContainer, getGraphsObj(e));
}
function getCSClosure(g, obj){ return function(g, obj){ return function(){ new ColumnSquares(g, obj); }; }(g, obj); }
function initNotifications() {
var ele = elements.notifications.querySelector('div'),
keepLimit = liveNotifications.limit, //number of notifications to keep
tmpl = template.notificationEvent,
evtMapping = {
'create:manager':'Manager Created',
'create:advertiser':'Advertiser Created',
'create:agency': 'Agency Created',
'create:campaign': 'Campaign Created',
'edit:campaign': 'Campaign Edited',
'delete:campaign':'Campaign Deleted / Archived',
'create:ad': 'Ad Created',
'delete:advertiser': 'Advertiser Deleted',
'delete:agency': 'Agency Deleted',
'user:signin': 'Logged In',
'security:delete':'**SECURITY WARNING**'
};
liveNotifications.on('*', function(data) {
if(data.type === 'user:signin' && data.user === 'system@meteora.co') return;
data = getSimpleJSONCopy(data);
var isNew = data.type.indexOf('create') > -1,
isEdit = data.type.indexOf('edit') > -1,
isBoring = data.type.indexOf('user') > -1;
data.type = evtMapping[data.type] || data.type;
data.cls = isNew ? 'mint' : isEdit ? 'cool' : isBoring ? 'stone' : 'hot';
data.ip = trimIP(data.ip);
if(typeof data.ts === 'number') data.ts = formatISODate(new Date(data.ts*1000));
var t = getElementFromString(tmpl, data);
events.add(t, 'click', switchAction.bind(data));
prependChild(ele, t, keepLimit);
});
function switchAction(e) {
if(e && e.preventDefault) e.preventDefault();
if(this.uid == null) return;
if(this.userType === 'advertiser') {
ga('send', 'event', 'Admin Notifications', 'Switch to advertiser', this.uid);
} else if(this.userType === 'agency') {
ga('send', 'event', 'Admin Notifications', 'Switch to agency', this.uid);
}
core.switchUser(this.uid);
}
function trimIP(ip) {
if(!ip) return;
var idx = ip.lastIndexOf(':');
if(idx === -1) return ip;
return ip.substr(0, idx);
}
}
function initGeneral(){
const g = elements.general,
downloadDropdownOpts = new DropdownObj([
{ title: 'Download', value: '-', placeholder: true },
{ title: 'CSV', value: 'csv' },
{ title: 'JSON', value: 'json' },
], '-', 'cool', 'cool', 'cool', true
),
reqData = {},
obj = {
'squares' : [
{ 'column' : 0, 'size' : 'one', 'title' : 'Total Advertisers', 'element' : getSquare('totalAdvertisers') },
{ 'column' : 1, 'size' : 'one', 'title' : 'Total Campaigns', 'element' : getSquare('totalCampaigns') },
{ 'column' : 2, 'size' : 'one', 'title' : 'Total Users Tracking', 'element' : getSquare('numberOfUsersTracking') },
{ 'column' : 3, 'size' : 'two', 'title' : 'Average Weekly Budget per Campaign', 'element' : getSquare('weeklyBudget') },
{ 'column' : 0, 'size' : 'one', 'title' : 'Total Number of Ads', 'element' : getSquare('numberOfAds') },
{ 'column' : 1, 'size' : 'one', 'title' : 'Number of Segments', 'element' : getSquare('numberOfSegments') },
{ 'column' : 2, 'size' : 'two', 'title' : 'Average Active Campaigns per Client', 'element' : getSquare('averageCampaignsPerClient') }
]
};
elements.fromInput = new CalendarInput(g.querySelector('.fromInput'), null, updateFromDate);
elements.toInput = new CalendarInput(g.querySelector('.toInput'), null, updateToDate);
element.downloadBtn = new Dropdown(g.querySelector('.downloadAnchor'), downloadDropdownOpts, downloadReport);
function updateFromDate(e) {
if (!e) return;
reqData.from = e.year + '-' + getFormattedDate(e.monthValue) + '-' + getFormattedDate(e.date);
}
function updateToDate(e) {
if (!e) return;
reqData.to = e.year + '-' + getFormattedDate(e.monthValue) + '-' + getFormattedDate(e.date);
}
function getFormattedDate(v) { if (v > 9) return v; else return '0' + v; }
function downloadReport(e) {
preventDefault(e);
if (e === '-') return;
const req = { allStats: { source: `allTheStats/${reqData.from}/${reqData.to}?fmt=${e}`, noID: true } };
new ApiManager(req, core.userID, () => pop('Pop!', 'Downloads', '/dashboard/downloads'));
}
setTimeout(function() {
getCSClosure(g.querySelector('.quickStats'), obj)();
}, 1);
}
function initExchange() {
if(!Object.keys(exData).length) return;
var e = elements.exchange,
dropdown = getElementFromString(''),
lastTable = null,
dropDownData = [],
tables = {},
costs = {};
function setTotal(k) {
var sp = dropdown.querySelector('span');
sp.innerHTML = costs[k];
}
e.appendChild(dropdown);
for(var k in exData) {
var ele = getElementFromString(''),
v = exData[k],
lr = v.rows[v.rows.length - 1];
dropDownData.push({ 'title': 'Exchange: ' + v.name, 'value': k });
NewMTable(ele, v);
tables[k] = ele.classList;
e.appendChild(ele);
costs[k] = lr ? lr.cost : 'n/a';
if(k === 'casale') {
lastTable = ele.classList;
lastTable.remove('noDisplay');
setTotal(k);
}
}
var dd = new Dropdown(dropdown, {
data: dropDownData,
options: {},
}, function(p) {
if(lastTable !== null) {
lastTable.add('noDisplay');
}
lastTable = tables[p];
lastTable.remove('noDisplay');
setTotal(p);
});
dd.setValue('Casale');
}
function ExchangeObject(d) {
this.reqs = 0;
this.bids = 0;
this.impressions = 0;
this.cost = 0;
this.columns = this.getColumns();
this.rows = [];
this.process(d);
}
ExchangeObject.prototype = {
rowSelect: false,
addRow: function(d, o) {
if(!d.length) return;
var imp = o.impressions,
winPer = o.bids > 0 ? (imp / o.bids) * 100 : 0,
bidPer = o.requests > 0 ? (o.bids / o.requests) * 100 : 0;
this.reqs += o.requests;
this.bids += o.bids;
this.impressions += imp;
this.cost += o.cost;
this.rows.push({
'day': d,
'reqs': {title: fmtNum(o.requests), value: o.requests},
'bids': {title: fmtNum(o.bids), value: o.bids},
'winPer': {title: fmtNum(winPer) + '%', value: winPer},
'bidPer': {title: fmtNum(bidPer) + '%', value: bidPer},
'imp': {title: fmtNum(imp), value: imp},
'cost': {title: '$' + o.cost.toFixed(4), value: o.cost},
});
},
getColumns: function() {
return {
'day': {key: 'day', sortable: true, title: 'Day', valueType: 'string', width: 140},
'reqs': {key: 'reqs', sortable: true, title: 'Reqs', valueType: 'number', width: 134},
'bids': {key: 'bids', sortable: true, title: 'Bids', valueType: 'number', width: 134},
'bidPer': {key: 'bidPer', sortable: true, title: 'Bid %', valueType: 'number', width: 120},
'imp': {key: 'imp', sortable: true, title: 'Imp', valueType: 'number', width: 134},
'winPer': {key: 'winPer', sortable: true, title: 'Win %', valueType: 'number', width: 120},
'cost': {key: 'cost', sortable: true, title: 'Media Cost', valueType: 'string', width: 134},
};
},
process: function(data) {
var dates = Object.keys(data);
dates.sort();
for(var i = 0; i < dates.length; i++) {
var d = data[dates[i]];
this.addRow(dates[i], d);
}
this.rows.push({
__ignore__: true,
day: 'TOTAL:',
reqs: '' + fmtNum(this.reqs) + '',
bids: '' + fmtNum(this.bids) + '',
winPer: '' + fmtNum((this.imps / this.bids) * 100) + '%',
bidPer: '' + fmtNum((this.bids / this.reqs) * 100) + '%',
imp: '' + fmtNum(this.impressions) + '',
cost: '' + '$' + this.cost.toFixed(4) + '',
});
this.maxPerPage = this.rows.length;
},
};
function initGraph(){
var e = elements;
e.impressions = getElementFromString('');
e.clicks = getElementFromString('');
e.conversions = getElementFromString('');
e.graphContainer = e.index.querySelector('.graphContainer');
new Chart(e.impressions.getContext('2d')).Line(data.graph.impressions);
new Chart(e.clicks.getContext('2d')).Line(data.graph.clicks);
new Chart(e.conversions.getContext('2d')).Line(data.graph.conversions);
}
function initPlatformAlert() {
var e = elements.platformAlert,
btn = e.querySelector('.saveButton'),
val = e.querySelector('textarea');
events.add(e, 'click', function() {
new HttpRequest('PUT', '/api/v1/adminMessage', {'message': val.value}, 'json', 'json', function(e, st) {
if(st !== 200) return core.notifications.setError('Couldn\'t set the platform alert');
core.notifications.setSuccess('Sucessfully set the plaform alert.');
});
});
}
function initHealth() {
var e = elements.health,
tbl = getElementFromString(''),
header = {
'server': {key: 'server', sortable: true, title: 'Server', valueType: 'string', width: 120},
'status': {key: 'status', sortable: true, title: 'Status', valueType: 'string', width: 420}
},
upSpan = 'healthy',
naSpan = 'not available',
downSpan = 'sick, reason: ';
function HealthObject() {
this.rowSelect = false;
this.columns = header;
this.rows = (health||[]).map(function(v) {
return {
server: v.name,
status: {noTitle: true, value: '-', title: v.isHealthy ? upSpan : v.error ? downSpan + v.error : naSpan}
};
});
}
NewMTable(tbl, new HealthObject());
e.appendChild(tbl);
}
function initSearch() {
var e = elements.search,
searchRes = e.querySelector('div:last-child'),
searchType = 'Agency',
searchInput = e.querySelector('input'),
dd = new Dropdown(e.querySelector('.searchFor'), {
data: makeOptions('Agency', 'Advertiser', 'Campaign'),
options: {},
}, function(v) { searchType = v; });
events.add(e.querySelector('a.go'), 'click', search);
events.add(searchInput, 'keypress', function(e) {if(e.keyCode === 13) search();});
function search() {
var url = '/api/v1/search/' + searchType.toLowerCase() + '?q=' + encodeURIComponent(searchInput.value);
new HttpRequest('GET', url, null, null, 'json', function(e, st) {
if(isArray(e)) e.forEach(function(v) { v.type = searchType; });
searchRes.innerHTML = render(template.searchResult, {items: e});
forEach(searchRes.querySelectorAll('item > a[data-uid]'), function(a) {
events.add(a, 'click', switchTo);
});
});
}
function switchTo(evt) {
evt.preventDefault();
var e = evt.target,
uid = e.dataset.uid,
cid = e.dataset.cid;
if(!uid) return;
ga('send', 'event', 'Admin Notifications', 'Switch to User', uid);
core.switchUser(uid, function(){ if(!!cid) pop('Pop!', 'Dashboard', '/campaigns/edit/' + cid); });
}
}
function makeOptions() {
return [].map.call(arguments, function(v) {
return {title: v, value: v};
});
}
function Data(d){ this.graph = isDefined(d.reporting) ? getGraphData(d) : null; }
function Squares(d,u){
var advertisers = { 'count' : d.totalAdvertisers, 'today' : d.newAdvertisers },
activeCampaigns = { 'count' : d.totalCampaigns, 'percent' : getPercentage(d.activeCampaigns, d.totalCampaigns) },
weeklyBudget = { 'average' : getRoundedValue(d.totalBudgets / d.totalCampaigns, 2), 'billedThisWeek' : 300 },
activeAds = { 'count' : d.totalAds, 'average' : getRoundedValue(d.totalAds / d.totalAdvertisers, 2) },
segments = { 'count' : d.totalSegments, 'average' : getRoundedValue(d.totalBudgets / d.totalSegments, 2) };
// General
this.notifications = { 'template' : template.notificationSquare, 'data' : notificationsData };
this.exchange = { 'template' : template.exchange };
this.totalAdvertisers = getSquareObj(template.totalSquare, advertisers.count, 'signed up today', advertisers.today);
this.numberOfUsersTracking = getSquareObj(template.totalSquare, u.views,
'Unique', u.uniqueUsers);
// Ads
this.numberOfAds = getSquareObj(template.totalSquare, activeAds.count, 'avg. per client', activeAds.average);
// Campaigns
this.totalCampaigns = getSquareObj(template.totalSquare, activeCampaigns.count, '% currently active', activeCampaigns.percent + '%');
this.averageCampaignsPerClient = getSquareObj(template.totalSquare, getRoundedValue(activeCampaigns.count / d.totalAdvertisers, 2), '% of total campaigns', activeCampaigns.percent + '%');
this.weeklyBudget = getSquareObj(template.totalSquare, weeklyBudget.average, 'billed this week', weeklyBudget.billedThisWeek);
// Segments
this.numberOfSegments = getSquareObj(template.totalSquare, segments.count, 'avg. per client', segments.average);
}
function getPercentage(a, b){ return Math.round(((a / b) * 10000)) / 100; }
function getRoundedValue(number, places){ return Math.round(number * Math.pow(10, places)) / Math.pow(10, places); }
function getSquareObj(template, mV, sT, sV){ return { 'template' : template, 'data' : { 'mainValue' : mV, 'subTitle' : sT, 'subValue' : sV } }; }
function getSquare(k){ var s = squares[k]; if(s.template) return getElementFromString(render(s.template, isDefined(s.data) ? s.data : {})); else return null; }
function getGraphData(d){
var userLevel = d.reporting.userLevel,
impressions = getValues(userLevel, 'impressions'),
clicks = getValues(userLevel, 'clicks'),
conversions = getValues(userLevel, 'conversions');
return {
'impressions' : {
labels : impressions.labels,
datasets : [
{
fillColor : 'rgba(151,187,205,0.5)',
strokeColor : 'rgba(151,187,205,1)',
pointColor : 'rgba(151,187,205,1)',
pointStrokeColor : '#fff',
data : impressions.data
}
]
},
'clicks' : {
labels : clicks.labels,
datasets : [
{
fillColor : 'rgba(151,187,205,0.5)',
strokeColor : 'rgba(151,187,205,1)',
pointColor : 'rgba(151,187,205,1)',
pointStrokeColor : '#fff',
data : clicks.data
}
]
},
'conversions' : {
labels : conversions.labels,
datasets : [
{
fillColor : 'rgba(151,187,205,0.5)',
strokeColor : 'rgba(151,187,205,1)',
pointColor : 'rgba(151,187,205,1)',
pointStrokeColor : '#fff',
data : conversions.data
}
]
}
};
function getDate(s){ var v = s.split(' '); return { 'day' : v[0], 'month' : v[1], 'year' : v[2]} ; }
function getValues(d, key){
var data = [],
labels = [],
listLength = 0;
for(var k in d){
var t = d[k][core.userID],
date = getDate(k);
data.push(t[key]);
labels.push(date);
}
listLength = data.length;
if(listLength > 30){
data = data.slice(listLength - 30);
labels = labels.slice(listLength - 30);
}
return getSortedReportingDates(labels, data);
}
}
function getTabbedObj(e){
var obj = {
'data' : [
// Enable notifications when ticket #475 is completed
{ 'title' : 'Quick Stats', 'element' : e.general, 'initFn' : initGeneral },
{ 'title' : 'Notifications', 'element' : e.notifications, 'initFn' : initNotifications },
]
};
if(Object.keys(exData).length) {
obj.data.push({ 'title' : 'Exchange Data', 'element' : e.exchange, 'initFn' : initExchange });
}
obj.data.push({ 'title' : 'Platform Alert', 'element' : e.platformAlert, 'initFn' : initPlatformAlert });
obj.data.push({ 'title' : 'Platform Health', 'element' : e.health, 'initFn' : initHealth });
obj.data.push({ 'title' : 'Search', 'element' : e.search, 'initFn' : initSearch });
return obj;
}
function getGraphsObj(e){
return {
'data' : [
{ 'title' : 'Impressions', 'element' : e.impressions },
{ 'title' : 'Clicks', 'element' : e.clicks },
{ 'title' : 'Conversions', 'element' : e.conversions }
],
'config' : {
'className' : 'toggle'
}
};
}
function insertToTarget(e){ target.appendChild(elements[e]); }
function exit(){
if(isDefined(element)) removeChild(target, element);
if(events) events.reset();
if(elements) elements.exit();
if(liveNotifications) liveNotifications.close();
housekeeping();
}
function housekeeping(){ core = target = data = returnCB = events = template = elements = data = squares = notificationsData = null; }
};