mirror of
https://github.com/gwenhael-le-moine/credger.git
synced 2024-12-26 09:58:36 +01:00
Merge branch 'master' of github.com:gwenhael-le-moine/credger
This commit is contained in:
commit
5072d97186
4 changed files with 147 additions and 40 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@
|
|||
/credger
|
||||
/public/js
|
||||
/.shards
|
||||
/bin/
|
||||
|
|
178
public/pure.html
178
public/pure.html
|
@ -1,55 +1,153 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
|
||||
const API = {
|
||||
balance: async (period, categories, depth) => {
|
||||
let url = new URL( "/api/ledger/balance", `${location.protocol}//${location.host}` );
|
||||
url.search = new URLSearchParams( { period: period,
|
||||
categories: categories,
|
||||
depth: depth } );
|
||||
fetch: async ( endpoint, params = {} ) => {
|
||||
let url = new URL( endpoint, `${location.protocol}//${location.host}` );
|
||||
url.search = new URLSearchParams( params );
|
||||
|
||||
const response = await fetch( url );
|
||||
const myJson = await response.json(); //extract JSON from the http response
|
||||
// do something with myJson
|
||||
console.log(myJson);
|
||||
},
|
||||
|
||||
register: async (period, categories) => {
|
||||
let url = new URL( "/api/ledger/register", `${location.protocol}//${location.host}` );
|
||||
url.search = new URLSearchParams( { period: period,
|
||||
categories: categories } );
|
||||
const response = await fetch( url );
|
||||
const myJson = await response.json(); //extract JSON from the http response
|
||||
// do something with myJson
|
||||
console.log(myJson);
|
||||
},
|
||||
|
||||
graph_values: async (period, categories, granularity) => {
|
||||
let url = new URL( "/api/ledger/graph_values", `${location.protocol}//${location.host}` );
|
||||
url.search = new URLSearchParams( { period: period,
|
||||
categories: categories,
|
||||
granularity: granularity } );
|
||||
const response = await fetch( url );
|
||||
const myJson = await response.json(); //extract JSON from the http response
|
||||
// do something with myJson
|
||||
console.log(myJson);
|
||||
},
|
||||
|
||||
accounts: async () => {
|
||||
let url = new URL( "/api/ledger/accounts", `${location.protocol}//${location.host}` );
|
||||
const response = await fetch( url );
|
||||
const myJson = await response.json(); //extract JSON from the http response
|
||||
// do something with myJson
|
||||
console.log(myJson);
|
||||
|
||||
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 = {
|
||||
text_to_color: text => {
|
||||
let hash = 0
|
||||
for (let i = 0; i < text.length; i++)
|
||||
hash = (((hash << 5) - hash) + text.charCodeAt(i)) | 0;
|
||||
hash = Math.abs( hash );
|
||||
|
||||
let shash = hash.toString(16).substr(0, 6).padEnd( 6, '0' );
|
||||
|
||||
return `#${shash}`;
|
||||
},
|
||||
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 UI = {
|
||||
/* https://medium.com/@heyoka/scratch-made-svg-donut-pie-charts-in-html5-2c587e935d72 */
|
||||
donut: ( element, dataset ) => {
|
||||
const thickness = 9;
|
||||
let filed_percent = 0;
|
||||
const data_to_donut_segment = ( data ) => {
|
||||
if ( data.amount > 0 ) {
|
||||
const stroke_dashoffset = ( percent ) => {
|
||||
let offset = 100 - filed_percent;
|
||||
|
||||
return offset > 100 ? offset - 100 : offset;
|
||||
};
|
||||
|
||||
const donut_segment = `<circle class="donut-segment ${data.account.split(':').join(' ')}" cx="21" cy="21" r="15.91549430918954" fill="transparent" stroke="${data.color}" stroke-width="${thickness}" stroke-dasharray="${data.percent} ${100 - data.percent}" stroke-dashoffset="${stroke_dashoffset( data.percent )}"><title>${data.tooltip}</title></circle>`;
|
||||
filed_percent += data.percent;
|
||||
|
||||
return donut_segment;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
element.innerHTML = `<svg width="100%" height="100%" viewBox="0 0 42 42" class="donut">
|
||||
<circle class="donut-hole" cx="21" cy="21" r="15.91549430918954" fill="#fff"></circle>
|
||||
<circle class="donut-ring" cx="21" cy="21" r="15.91549430918954" fill="transparent" stroke="#d2d3d4" stroke-width="${thickness}"></circle>
|
||||
|
||||
${dataset.map( line => data_to_donut_segment( line ) ).join("")}
|
||||
</svg>`;
|
||||
}
|
||||
};
|
||||
|
||||
let selected_accounts = [];
|
||||
let current_period;
|
||||
|
||||
const Controls = {
|
||||
period: {
|
||||
init: () => Controls.period.set( new Date() ),
|
||||
set: period => {
|
||||
current_period = period;
|
||||
|
||||
document.querySelector( "#period #display" ).innerHTML = Utils.readable_date( current_period );
|
||||
|
||||
monthly( current_period, selected_accounts );
|
||||
},
|
||||
get: () => current_period,
|
||||
prev: () => {
|
||||
current_period.setMonth( current_period.getMonth() - 1 );
|
||||
Controls.period.set( current_period );
|
||||
},
|
||||
next: () => {
|
||||
current_period.setMonth( current_period.getMonth() + 1 );
|
||||
Controls.period.set( current_period );
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
init: async () => {
|
||||
let account_to_option = ( account, selected ) => `<option value="${account.join(':')}" ${selected ? "selected" : ""}>${account.join(':')}</option>`;
|
||||
let accounts = await API.accounts();
|
||||
let select = document.querySelector("select#accounts");
|
||||
select.innerHTML = '';
|
||||
|
||||
for ( let i = 1 ; i < accounts.reduce( (memo, a) => a.length > memo ? a.length : memo, 0 ) ; i++ ) {
|
||||
select.innerHTML += `<optgroup label="Depth: ${i}">${accounts.filter( a => a.length == i ).map( account => account_to_option( account, i == 1 )).join('')}</optgroup>`;
|
||||
}
|
||||
},
|
||||
onchange: accounts_selected => {
|
||||
selected_accounts = accounts_selected;
|
||||
|
||||
monthly( current_period, selected_accounts );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const monthly = async ( period, categories ) => {
|
||||
let balance = await API.balance( period.toISOString().split("T")[0].slice( 0, -3 ), categories.join(" "), 999 )
|
||||
const total = balance.reduce( (memo, line) => memo + line.amount, 0 );
|
||||
|
||||
UI.donut( document.querySelector( `#month #donut` ),
|
||||
balance.sort( (a, b) => b.amount - a.amount )
|
||||
.map( line => {
|
||||
line.color = Utils.text_to_color( line.account );
|
||||
line.percent = ( line.amount / total ) * 100;
|
||||
line.tooltip = `${line.account} : ${line.amount} €`;
|
||||
|
||||
return line;
|
||||
} ) );
|
||||
|
||||
console.log( await API.graph_values( "", selected_accounts.join(" "), "monthly" ) )
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
API.balance( "2019", ["Expenses", "Assets"].join(" "), 2 );
|
||||
<div id="controls">
|
||||
<div id="period">
|
||||
<button onclick="Controls.period.prev()">-</button>
|
||||
<button onclick="Controls.period.next()">+</button>
|
||||
<h3 id="display"></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
API.accounts();
|
||||
<select id="accounts" name="accounts" multiple size="20" onchange="Controls.accounts.onchange( Array.from( this.options ).filter( o => o.selected ).map( o => o.value ) )"></select>
|
||||
|
||||
<div id="month">
|
||||
<div id="donut" style="height: 256; width: 256;"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(async () => {
|
||||
await Controls.accounts.init();
|
||||
await Controls.period.init();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
version: 2.0
|
||||
shards:
|
||||
ameba:
|
||||
github: crystal-ameba/ameba
|
||||
version: 0.10.1
|
||||
|
||||
exception_page:
|
||||
git: https://github.com/crystal-loot/exception_page.git
|
||||
version: 0.1.5
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
name: credger
|
||||
version: 1.99
|
||||
|
||||
development_dependencies:
|
||||
ameba:
|
||||
github: crystal-ameba/ameba
|
||||
|
||||
dependencies:
|
||||
kemal:
|
||||
github: kemalcr/kemal
|
||||
|
|
Loading…
Reference in a new issue