app.component('dashboard', { controller: ['$filter', 'API', function($filter, API) { let ctrl = this; ctrl.granularity = "monthly"; ctrl.account_selection = "depth"; let is_monthly = () => { return ctrl.granularity == "monthly"; }; ctrl.compute_selected_accounts = () => { ctrl.graphed_accounts = _.chain(ctrl.main_accounts_depths) .map((account) => { if (account.depth < 1) { return null; } else { return _(ctrl.raw_accounts) .select((account2) => { return account2[0] == account.name && account2.length == account.depth; }) .map((account3) => { return account3.join(":"); }); } }) .compact() .flatten() .value(); ctrl.retrieve_graph_values(ctrl.graphed_accounts); }; ctrl.retrieve_graph_values = (categories) => { API.graph_values("", ctrl.granularity, categories.join(" ")) .then((response) => { ctrl.periods = []; _.chain(response.data) .reduce((memo, cat) => { return cat.length > memo.length ? cat : memo; }, []) .pluck('date') .each((date) => { _(response.data).each((cat) => { let value = _(cat).find({ date: date }); if (_(value).isUndefined()) { cat.push({ date: date, amount: 0, currency: _(cat).first().currency }); } }); }) .each((cat) => { cat = _(cat).sortBy((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: (t) => { console.log(ctrl.period) ctrl.period = t.pointXValue; console.log(ctrl.period) } }, tooltip: { contentGenerator: function(e) { let format_line = (serie) => { return ` <tr> <td style="background-color: ${serie.color}"> </td> <td>${serie.key}</td> <td style="text-align: right; font-weight: bold;">${serie.value}</td> </tr> `; }; let prepare_series = (series) => { series.sort((s1, s2) => { return s2.value - s1.value; }); return series.filter((s) => { return s.value != 0; }); }; let total = e.series.reduce((memo, serie) => { return memo + serie.value; }, 0); return ` <h2>${e.value}</h2> <table> <tbody> ${prepare_series(e.series).map((s) => { return format_line(s); }).join("")} </tbody> <tfoot> <tr style="color: #ececec; background-color: ${total < 0 ? 'green' : 'red'}"> <td> </td> <td style="text-align: right; text-decoration: underline; font-weight: bold;">Total</td> <td style="text-align: right; font-weight: bold;">${total}</td> </tr> </tfoot> </table> `; } } } } }, data: _.chain(response.data) .keys() .reverse() .map((key) => { return { key: key, values: _.chain(response.data[key]) .map((value) => { let date = new Date(value.date); let 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((item) => { return item.x; }) .value() }; }) .value() }; ctrl.periods = _.chain(ctrl.periods).uniq().sort().reverse().value(); ctrl.period = _(ctrl.periods).first(); }); }; API.accounts() .then((response) => { ctrl.raw_accounts = response.data.sort((account) => { return account.length; }).reverse(); ctrl.accounts = ctrl.raw_accounts.map((account_ary) => { return account_ary.join(':'); }); ctrl.main_accounts_depths = _.chain(ctrl.raw_accounts) .select((account) => { return account.length == 1; }) .map((account) => { return { name: account[0], depth: _(['Expenses']).contains(account[0]) ? 2 : _(['Income']).contains(account[0]) ? 1 : 0, max_depth: _.chain(ctrl.raw_accounts) .select((account2) => { return account2[0] == account[0] }) .reduce((memo, account3) => { return account3.length > memo ? account3.length : memo; }, 0) .value() }; }) .value(); ctrl.compute_selected_accounts(); }); } ], template: ` <div class="dashboard"> <div class="global-graph" style="height: 450px;"> <div class="accounts" style="width: 20%; height: 100%; float: left;"> <div style="width: 100%; float: left;"> <label><input type="radio" ng:model="$ctrl.account_selection" value="depth" name="depth"/>depth</label> <label><input type="radio" ng:model="$ctrl.account_selection" value="list" name="list"/>list</label> </div> <div style="width: 100%; height: 90%; float: left;"> <ul ng:if="$ctrl.account_selection == 'depth'"> <li ng:repeat="account in $ctrl.main_accounts_depths"> <label>{{account.name}} depth</label> <rzslider rz-slider-options="{floor: 0, ceil: account.max_depth, onEnd: $ctrl.compute_selected_accounts}" rz-slider:model="account.depth"></rzslider> </li> </ul> <select style="height: 100%; width: 100%;" multiple ng:model="$ctrl.graphed_accounts" ng:change="$ctrl.retrieve_graph_values($ctrl.graphed_accounts)" ng:if="$ctrl.account_selection == 'list'"> <option ng:repeat="account in $ctrl.accounts">{{account}}</option> </select> </div> <div style="width: 100%; float: left;"> <label><input type="radio" ng:model="$ctrl.granularity" value="monthly" name="monthly" ng:change="$ctrl.compute_selected_accounts()" />monthly</label> <label><input type="radio" ng:model="$ctrl.granularity" value="yearly" name="yearly" ng:change="$ctrl.compute_selected_accounts()" />yearly</label> </div> </div> <div class="graph" style="width: 80%; float: left;"> <nvd3 data="$ctrl.graphique.data" options="$ctrl.graphique.options"> </nvd3> </div> </div> <h1 style="text-align: center;"> <select ng:options="p as p for p in $ctrl.periods" ng:model="$ctrl.period"></select> </h1> <bucket categories="'Expenses Income Equity Liabilities'" period="$ctrl.period"></bucket> </div> ` });