credger/public/ts/components/dashboard.ts
2018-05-19 10:54:11 +02:00

217 lines
8.2 KiB
TypeScript

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>
`
});