var app = angular.module('app', ['ui.router', 'nvd3', 'angularMoment', 'chieffancypants.loadingBar', 'rzModule', ]) .config(['$stateProvider', '$urlRouterProvider', function ($stateProvider, $urlRouterProvider) { $stateProvider .state('app', { url: '', component: 'dashboard' }); } ]); app.service('API', ['$http', function ($http) { var API = this; API.balance = function (period, categories, depth) { return $http.get('/api/ledger/balance', { params: { period: period, categories: categories, depth: depth } }); }; API.register = function (period, categories) { return $http.get('/api/ledger/register', { params: { period: period, categories: categories } }); }; API.graph_values = function (period, granularity, categories) { return $http.get('/api/ledger/graph_values', { params: { period: period, granularity: granularity, categories: categories } }); }; API.accounts = _.memoize(function () { return $http.get('/api/ledger/accounts'); }); }]); app.component('bucket', { bindings: { categories: '<', period: '<' }, controller: ['$filter', 'API', function ($filter, API) { var ctrl = this; ctrl.depth = 99; ctrl.graph_options = { chart: { type: 'multiBarHorizontalChart', height: 600, margin: { top: 20, right: 20, bottom: 20, left: 200 }, x: function (d) { return d.account; }, y: function (d) { return d.amount; }, valueFormat: function (d) { return d + " \u20AC"; }, showYAxis: false, showValues: true, showLegend: true, showControls: false, showTooltipPercent: true, duration: 500, labelThreshold: 0.01, labelSunbeamLayout: true, labelsOutside: true, multibar: { dispatch: { elementClick: function (event) { API.register(ctrl.period, event.data.account) .then(function success(response) { var format_transaction = function (transaction) { return "\n \n " + transaction.date + "\n " + transaction.payee + "\n " + transaction.amount + " " + transaction.currency + "\n "; }; swal({ title: response.data.key, html: "\n \n \n \n \n \n \n \n " + response.data.values.map(function (transaction) { return format_transaction(transaction); }).join("") + "\n \n \n
DatePayeeAmount
Total" + event.data.amount + " \u20AC
" }); }, function error(response) { alert("error!"); }); } } } } }; ctrl.$onChanges = function (changes) { if (changes.period && changes.period.currentValue != undefined) { API.balance(ctrl.period, ctrl.categories, ctrl.depth) .then(function (response) { ctrl.raw_data = _(response.data) .sortBy(function (account) { return account.name; }); ctrl.graph_options.chart.height = 60 + (25 * ctrl.raw_data.length); ctrl.data = ctrl.categories.split(' ').map(function (category) { return { key: category, values: _(ctrl.raw_data).select(function (line) { return line.account.match("^" + category + ":.*"); }) }; }); }); } }; } ], template: "\n
\n
\n
\n \n \n
\n
\n
\n" }); app.component('dashboard', { controller: ['$filter', 'API', function ($filter, API) { var ctrl = this; ctrl.granularity = "monthly"; ctrl.account_selection = "depth"; var is_monthly = function () { return ctrl.granularity == "monthly"; }; ctrl.compute_selected_accounts = function () { ctrl.graphed_accounts = _.chain(ctrl.main_accounts_depths) .map(function (account) { if (account.depth < 1) { return null; } else { return _(ctrl.raw_accounts) .select(function (account2) { return account2[0] == account.name && account2.length == account.depth; }) .map(function (account3) { return account3.join(":"); }); } }) .compact() .flatten() .value(); ctrl.retrieve_graph_values(ctrl.graphed_accounts); }; ctrl.retrieve_graph_values = function (categories) { API.graph_values("", ctrl.granularity, categories.join(" ")) .then(function (response) { ctrl.periods = []; _.chain(response.data) .reduce(function (memo, cat) { return cat.length > memo.length ? cat : memo; }, []) .pluck('date') .each(function (date) { _(response.data).each(function (cat) { var value = _(cat).find({ date: date }); if (_(value).isUndefined()) { cat.push({ date: date, amount: 0, currency: _(cat).first().currency }); } }); }) .each(function (cat) { cat = _(cat).sortBy(function (month) { return month.date; }); }); ctrl.graphique = { options: { chart: { type: 'multiBarChart', height: 450, stacked: true, showControls: true, showLegend: true, showLabels: true, showValues: true, showYAxis: true, duration: 500, reduceXTicks: false, rotateLabels: -67, labelSunbeamLayout: true, useInteractiveGuideline: true, interactiveLayer: { dispatch: { elementClick: function (t) { console.log(ctrl.period); ctrl.period = t.pointXValue; console.log(ctrl.period); } }, tooltip: { contentGenerator: function (e) { var format_line = function (serie) { return "\n\n \n" + serie.key + "\n" + serie.value + "\n\n"; }; var prepare_series = function (series) { series.sort(function (s1, s2) { return s2.value - s1.value; }); return series.filter(function (s) { return s.value != 0; }); }; var total = e.series.reduce(function (memo, serie) { return memo + serie.value; }, 0); return "\n

" + e.value + "

\n\n \n " + prepare_series(e.series).map(function (s) { return format_line(s); }).join("") + "\n \n \n \n \n \n \n \n \n
Total" + total + "
\n"; } } } } }, data: _.chain(response.data) .keys() .reverse() .map(function (key) { return { key: key, values: _.chain(response.data[key]) .map(function (value) { var date = new Date(value.date); var period = is_monthly() ? date.getFullYear() + '-' + (date.getMonth() < 9 ? '0' : '') + (date.getMonth() + 1) : date.getFullYear(); ctrl.periods.push(period); return { key: key, x: period, y: parseInt(value.amount) }; }) .sortBy(function (item) { return item.x; }) .value() }; }) .value() }; ctrl.periods = _.chain(ctrl.periods).uniq().sort().reverse().value(); ctrl.period = _(ctrl.periods).first(); }); }; API.accounts() .then(function (response) { ctrl.raw_accounts = response.data.sort(function (account) { return account.length; }).reverse(); ctrl.accounts = ctrl.raw_accounts.map(function (account_ary) { return account_ary.join(':'); }); ctrl.main_accounts_depths = _.chain(ctrl.raw_accounts) .select(function (account) { return account.length == 1; }) .map(function (account) { return { name: account[0], depth: _(['Expenses']).contains(account[0]) ? 2 : _(['Income']).contains(account[0]) ? 1 : 0, max_depth: _.chain(ctrl.raw_accounts) .select(function (account2) { return account2[0] == account[0]; }) .reduce(function (memo, account3) { return account3.length > memo ? account3.length : memo; }, 0) .value() }; }) .value(); ctrl.compute_selected_accounts(); }); } ], template: "\n
\n
\n
\n
\n \n \n
\n
\n
    \n
  • \n \n \n
  • \n
\n\n \n
\n\n
\n \n \n
\n
\n\n
\n \n \n
\n
\n\n

\n \n

\n\n \n
\n" });