mirror of
https://github.com/gwenhael-le-moine/credger.git
synced 2024-12-26 09:58:36 +01:00
fe4ac79498
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
156 lines
7.1 KiB
HTML
156 lines
7.1 KiB
HTML
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
|
|
<!-- Plotly.js -->
|
|
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
<script>
|
|
let selected_accounts = [];
|
|
|
|
const API = {
|
|
fetch: async ( endpoint, params = {} ) => {
|
|
let url = new URL( endpoint, `${location.protocol}//${location.host}` );
|
|
url.search = new URLSearchParams( params );
|
|
|
|
const response = await fetch( url );
|
|
|
|
return await response.json();
|
|
},
|
|
|
|
balance: ( period, categories, depth ) => API.fetch( "/api/ledger/balance", { period: period, categories: categories, depth: depth } ),
|
|
register: ( period, categories ) => API.fetch( "/api/ledger/register", { period: period, categories: categories } ),
|
|
graph_values: ( period, categories, granularity ) => API.fetch( "/api/ledger/graph_values", { period: period, categories: categories, granularity: granularity } ),
|
|
accounts: () => API.fetch( "/api/ledger/accounts" )
|
|
}
|
|
|
|
const Utils = {
|
|
readable_date: date => {
|
|
const months = { 0: 'Janvier', 1: 'Février', 2: 'Mars', 3: 'Avril', 4: 'Mai', 5: 'Juin', 6: 'Juillet', 7: 'Août', 8: 'Septembre', 9: 'Octobre', 10: 'Novembre', 11: 'Décembre' };
|
|
|
|
return `${months[ date.getMonth() ]} ${date.getFullYear()}`;
|
|
}
|
|
};
|
|
|
|
const Controls = {
|
|
accounts_list: {
|
|
init: async ( ui_id ) => {
|
|
let accounts = await API.accounts();
|
|
let ui_accounts = document.querySelector(`#${ui_id}`);
|
|
let content = "<select name=\"accounts\" multiple size=\"20\">";
|
|
|
|
let account_to_option = ( account, selected ) => `<option value="${account.join(':')}" ${selected ? "selected" : ""}>${account.join(':')}</option>`;
|
|
|
|
for ( let i = 1 ; i < accounts.reduce( (memo, a) => a.length > memo ? a.length : memo, 0 ) ; i++ ) {
|
|
content += `
|
|
<optgroup label="Depth: ${i}">
|
|
${accounts.filter( a => a.length == i ).map( account => account_to_option( account, ((account.length == 1 && account[0] == "Assets") || (account.length == 2 && account[0] == "Expenses")))).join('')}
|
|
</optgroup>`;
|
|
}
|
|
content += "</select>";
|
|
|
|
ui_accounts.innerHTML = content;
|
|
|
|
let ui_accounts_select = document.querySelector(`#${ui_id} select`);
|
|
|
|
ui_accounts_select.addEventListener( 'change', event => Controls.accounts_list.onchange( event.target ) );
|
|
Controls.accounts_list.onchange( ui_accounts_select );
|
|
},
|
|
onchange: element => {
|
|
selected_accounts = Array.from( element.options ).filter( o => o.selected ).map( o => o.value );
|
|
|
|
Controls.main_graph.set( selected_accounts );
|
|
}
|
|
},
|
|
|
|
accounts_sliders: {
|
|
init: async ( ui_id ) => {
|
|
let accounts = await API.accounts();
|
|
let ui_accounts = document.querySelector(`#${ui_id}`);
|
|
let content = "<select name=\"accounts\" multiple size=\"20\">";
|
|
|
|
let accounts_depths = {};
|
|
|
|
let onchange = () => {
|
|
selected_accounts = Object.keys( accounts_depths )
|
|
.filter( act => accounts_depths[act] > 0 )
|
|
.map( act => accounts.filter( a => a[0] == act && a.length == accounts_depths[act] ) )
|
|
.flat();
|
|
|
|
Controls.main_graph.set( selected_accounts );
|
|
};
|
|
|
|
ui_accounts.innerHTML = accounts.filter( act => act.length == 1 ).map( act => {
|
|
let max_depth = accounts.filter(a => a[0] == act[0]).map(a => a.length).sort().reverse()[0];
|
|
let val = 0;
|
|
if (act[0] == "Income")
|
|
val = 1;
|
|
else if (act[0] == "Expenses")
|
|
val = 2;
|
|
|
|
accounts_depths[ act[0] ] = val;
|
|
|
|
return `<div style="float: left; width: 100%;"><label for="myRange-${act[0]}" style="float: left;"">${act}</label><input style="float: right;" type="range" min="0" max="${max_depth}" value="${val}" class="slider" name="myRange-${act[0]}" id="myRange-${act[0]}"></div>`;
|
|
}).join('');
|
|
|
|
Object.keys( accounts_depths ).forEach( act => {
|
|
document.querySelector( `#myRange-${act}` )
|
|
.addEventListener( 'change', event => {
|
|
accounts_depths[act] = event.target.value;
|
|
|
|
onchange();
|
|
});
|
|
});
|
|
|
|
onchange();
|
|
}
|
|
},
|
|
|
|
main_graph: {
|
|
init: ( ui_id ) => {
|
|
let layout = {
|
|
xaxis: {title: 'Dates'},
|
|
yaxis: {title: 'Montant'},
|
|
barmode: 'relative',
|
|
title: 'Relative Barmode'
|
|
};
|
|
|
|
Controls.main_graph.set = async ( accounts ) => {
|
|
console.log(accounts)
|
|
if (accounts.length < 1)
|
|
return;
|
|
|
|
let values = await API.graph_values( "", accounts.map(a => a.join(':')).join(" "), "monthly" );
|
|
|
|
Plotly.react( ui_id,
|
|
Object.keys(values)
|
|
.map( act => {
|
|
let v = values[ act ].sort( (a, b) => b.date - a.date );
|
|
return {
|
|
name: act,
|
|
type: "bar",
|
|
x: v.map( reg => new Date( reg.date ) ), //Utils.readable_date( new Date( reg.date ) ) ),
|
|
y: v.map( reg => reg.amount)
|
|
};
|
|
} ),
|
|
layout );
|
|
};
|
|
}
|
|
},
|
|
};
|
|
</script>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="accounts" style="float: left; width: 19%;"></div>
|
|
|
|
<div id="mainGraph" style="float: right; width: 79%;" ></div>
|
|
|
|
<script>
|
|
(async () => {
|
|
Controls.main_graph.init( "mainGraph" );
|
|
// Controls.accounts_list.init( "accounts" );
|
|
Controls.accounts_sliders.init( "accounts" );
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|