diff options
author | Minteck <contact@minteck.org> | 2022-06-23 13:58:23 +0200 |
---|---|---|
committer | Minteck <contact@minteck.org> | 2022-06-23 13:58:23 +0200 |
commit | d2a4546fdb55ac8dba8d41d3a7dad39e68555c81 (patch) | |
tree | d60af37a24c326a295e40e49c473591b0f909318 /index.html | |
parent | d86e7133b5653409d56cb82b87b947a5ff5f6d67 (diff) | |
download | bits-client-d2a4546fdb55ac8dba8d41d3a7dad39e68555c81.tar.gz bits-client-d2a4546fdb55ac8dba8d41d3a7dad39e68555c81.tar.bz2 bits-client-d2a4546fdb55ac8dba8d41d3a7dad39e68555c81.zip |
Add stats
Diffstat (limited to 'index.html')
-rw-r--r-- | index.html | 138 |
1 files changed, 125 insertions, 13 deletions
@@ -13,6 +13,7 @@ } * { user-select: none; + overflow-x: hidden !important; } body { color: white; @@ -41,18 +42,18 @@ } </style> <link rel="stylesheet" href="./assets/black.css" disabled id="mobile-css"> + <script src="./chart.js"></script> + <script src="./trendline.js"></script> </head> <body style="position:fixed;inset:0;"> <div id="loader" style="background:#222;transition:opacity 500ms;z-index:9999;position:fixed;inset:0;display:flex;align-items: center;justify-content: center;"> - <img src="assets/logo.svg" style="width:96px;"> + <img alt="" src="assets/logo.svg" style="width:96px;"> <script> window.onload = async () => { if (!isNodeJS) { document.getElementById("mobile-css").removeAttribute("disabled"); } - document.body.innerHTML = document.body.innerHTML.replace(/(\n| {2,})/gm, ""); - setTimeout(async () => { loginStatus = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Test/")).text()).status; if (loginStatus === 1) { @@ -74,7 +75,7 @@ async function refresh() { document.getElementById("transactions").innerHTML = ""; - document.getElementById("username").innerText = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username")).text()).name; + document.getElementById("username").innerText = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username/")).text()).name; document.getElementById("user-profile").src = "https://account.minteck.org/hub/api/rest/avatar/" + JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Authentication/Username/")).text()).id + "?dpr=2&size=48"; window.transactions = JSON.parse(await (await window.fetch("https://money-v1.equestria.dev/Application/TransactionsList/")).text()); @@ -127,7 +128,6 @@ totalPaidGBP = 0; } - try { totalGainedEUR = transactions.filter(i => i.type !== "pay").map(i => { return i.amount['eur']; }).reduce((a, b) => { return a+b; }); } catch (e) { @@ -168,6 +168,67 @@ deleteTransaction(item.getAttribute("data-transaction-id")); } } + + graph.data.labels = transactions.map((i) => { return new Date(i.date.absolute).toString().split(":")[0] + ":" + new Date(i.date.absolute).toString().split(":")[1]; }).reverse().filter((_, i) => i > 1); + + let last = 0; + let balances = []; + + transactions.map((i) => { if (i.type === "pay") { return -(i.amount.eur); } else { return i.amount.eur; } }).reverse().map((i) => { + last = last + i; + balances.push(last); + }); + graph.data.datasets[0].data = balances.filter((_, i) => i > 1); + + trendData = balances.filter((_, i) => i > 1).map((i, _) => { return { x: _, y: i } }); + trend = trendline(trendData, 'x', 'y'); + + let lastTrend = trend.yStart; + let balancesTrend = []; + + transactions.filter((_, i) => i > 1).map(() => { + lastTrend = lastTrend + trend.slope; + balancesTrend.push(lastTrend); + }); + + graph.data.datasets[1].data = balancesTrend; + + document.getElementById("graph-insights-color").style.backgroundColor = "black"; + document.getElementById("graph-insights-text").style.color = "black"; + document.getElementById("graph-insights-text").innerText = "No insights available. Please try again later."; + + let avgSlope = Math.round(trend.slope); + if (avgSlope < -3) { + document.getElementById("graph-insights-color").style.backgroundColor = "red"; + document.getElementById("graph-insights-text").style.color = "red"; + document.getElementById("graph-insights-text").innerText = "Money is going down faster than it should, you must immediately reduce your expenses."; + } else if (avgSlope < 0) { + document.getElementById("graph-insights-color").style.backgroundColor = "orange"; + document.getElementById("graph-insights-text").style.color = "orange"; + document.getElementById("graph-insights-text").innerText = "You are not saving money, consider reducing your expenses."; + } else if (avgSlope === 0) { + document.getElementById("graph-insights-color").style.backgroundColor = "green"; + document.getElementById("graph-insights-text").style.color = "green"; + document.getElementById("graph-insights-text").innerText = "Your balance is stable, consider saving more money."; + } else if (avgSlope > 0) { + document.getElementById("graph-insights-color").style.backgroundColor = "sky"; + document.getElementById("graph-insights-text").style.color = "sky"; + document.getElementById("graph-insights-text").innerText = "You are effectively saving money."; + } + + if (avgSlope < 0) { + it = 0; b = totalEUR; while (b > 0) { it++; b = b + trend.slope; } + let avgDelay = Math.round(averageDelta(transactions.map((i) => { return new Date(i.date.absolute).getTime() }).reverse())); + let timeUntilEmpty = avgDelay * it; + let date = new Date(new Date().getTime() + timeUntilEmpty).toString().split(":")[0]; + document.getElementById("graph-insights-text").innerText += " (reaching zero on " + date.substring(0, date.length - 3) + ")"; + } else { + it = 0; b = totalEUR; while (b < goal.amount.eur) { it++; b = b + trend.slope; } + let avgDelay = Math.round(averageDelta(transactions.map((i) => { return new Date(i.date.absolute).getTime() }).reverse())); + let timeUntilGoal = avgDelay * it; + let date = new Date(new Date().getTime() + timeUntilGoal).toString().split(":")[0]; + document.getElementById("graph-insights-text").innerText += " (reaching goal on " + date.substring(0, date.length - 3) + ")"; + } } async function createTransaction() { @@ -236,19 +297,15 @@ <div id="header" style="z-index:5;background: #111;position: fixed;top: 0;padding: 8px 30px;left: 0;right: 0;height: 32px;"> <img alt="Bits" src="assets/logo.svg" style="width:32px;vertical-align: middle;"> <span style="vertical-align: middle;" id="menu-desktop"> - <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">New transaction</a> - <a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a> + <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">New transaction</a><a class="ln" id="tab-0" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = 'none';document.getElementById('tab-0').style.display = 'none';document.getElementById('tab-1').style.display = '';document.getElementById('graph').style.display = '';">Statistics</a><a class="ln" id="tab-1" style="display:none;cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = '';document.getElementById('tab-0').style.display = '';document.getElementById('tab-1').style.display = 'none';document.getElementById('graph').style.display = 'none';">Transactions</a><a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a> </span> <span style="vertical-align: middle;display:none;" id="menu-mobile"> - <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">Add</a> - <a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a> + <a class="ln" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('create-modal').style.display = isNodeJS ? 'flex' : '';">Add</a><a class="ln" id="tab-0m" style="cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = 'none';document.getElementById('tab-0m').style.display = 'none';document.getElementById('tab-1m').style.display = '';document.getElementById('graph').style.display = '';">Stats</a><a class="ln" id="tab-1m" style="display:none;cursor:pointer;margin-left:20px;" onclick="document.getElementById('list').style.display = '';document.getElementById('tab-0m').style.display = '';document.getElementById('tab-1m').style.display = 'none';document.getElementById('graph').style.display = 'none';">List</a><a class="ln" onclick="document.getElementById('about-modal').style.display = isNodeJS ? 'flex' : '';" style="cursor:pointer;margin-left:20px;">About</a> </span> <span style="float: right;margin-right: 10px;margin-top: 7px;" id="total-data"> <span id="balance" style="opacity: .5;"> <span id="balance-eur">%total_eur%</span>€, £<span id="balance-gbp">%total_gbp%</span> - </span> - <span id="username-desktop" style="opacity:.5;"> · <span id="username">%user%</span></span> - <span id="username-mobile" style="display:none;vertical-align: middle;"><img id="user-profile" alt="" style="border-radius: 999px;width: 32px;height: 32px;vertical-align: middle;position:relative;top:-6px;display:inline-block;margin-left:7px;" src=""></span> + </span><span id="username-desktop" style="opacity:.5;"> · <span id="username">%user%</span></span><span id="username-mobile" style="display:none;vertical-align: middle;"><img id="user-profile" alt="" style="border-radius: 999px;width: 32px;height: 32px;vertical-align: middle;position:relative;top:-6px;display:inline-block;margin-left:7px;" src=""></span> </span> </div> <script> @@ -273,12 +330,67 @@ <div class="transaction-details"> <b>%user%</b> %type% <span style="color:var(--perc-color);"><b>%amount_bc%</b> <i>(%amount_cc%)</i></span><br> %time%<br> - <div style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;width: 429px;">%description%</div> + <div style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;width: 100%;">%description%</div> </div> </div> </div> <div id="transactions" style="overflow: scroll;height: 100%;"></div> </div> + + <div id="graph" style="display:none;width:100%;height:100%;"> + <p style=" + font-size: 14px; + margin: 14px; +"> + <span id="graph-insights-color" style="display: inline-block;width: 12px;height: 12px;border-radius: 999px;opacity: .75;background: black;vertical-align: middle;margin-right: 5px;"></span> + <span id="graph-insights-text" style=" + display: inline-block; + vertical-align: middle; +">No insights available. Please try again later.</span> + </p> + <canvas id="graph-display" style="width:100%;height:100%;"></canvas> + <script> + const ctx = document.getElementById('graph-display').getContext('2d'); + const graph = new Chart(ctx, { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Balance', + data: [], + borderColor: 'rgba(153, 102, 255, 1)', + fill: false, + }, + { + label: 'Trendline', + data: [], + borderColor: 'rgba(153, 102, 255, .5)', + borderDash: [5], + pointRadius: 0, + fill: false, + }] + }, + options: { + scales: { + y: { + beginAtZero: true + } + }, + scaleLabel: "<%=value%>%", + legend: { + display: false + }, + tooltips: { + callbacks: { + label: function(tooltipItem, data) { + return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + '€'; + } + } + } + } + }); + </script> + </div> </div> <div id="create-modal" style=" |