summaryrefslogtreecommitdiff
path: root/index.html
diff options
context:
space:
mode:
authorMinteck <contact@minteck.org>2022-06-23 13:58:23 +0200
committerMinteck <contact@minteck.org>2022-06-23 13:58:23 +0200
commitd2a4546fdb55ac8dba8d41d3a7dad39e68555c81 (patch)
treed60af37a24c326a295e40e49c473591b0f909318 /index.html
parentd86e7133b5653409d56cb82b87b947a5ff5f6d67 (diff)
downloadbits-client-d2a4546fdb55ac8dba8d41d3a7dad39e68555c81.tar.gz
bits-client-d2a4546fdb55ac8dba8d41d3a7dad39e68555c81.tar.bz2
bits-client-d2a4546fdb55ac8dba8d41d3a7dad39e68555c81.zip
Add stats
Diffstat (limited to 'index.html')
-rw-r--r--index.html138
1 files changed, 125 insertions, 13 deletions
diff --git a/index.html b/index.html
index 0112e42..fb29374 100644
--- a/index.html
+++ b/index.html
@@ -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="