Skip to content

Commit

Permalink
Add more statistics with graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
ellite committed Nov 15, 2023
1 parent 602387d commit 190c052
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 28 deletions.
20 changes: 20 additions & 0 deletions scripts/libs/chart.js

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions scripts/stats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
function loadGraph(container, dataPoints, symbol, run) {
if (run) {
var ctx = document.getElementById(container).getContext('2d');

var chart = new Chart(ctx, {
type: 'pie',
data: {
datasets: [{
data: dataPoints.map(point => point.y),
}],
labels: dataPoints.map(point => `${point.label} (${point.y}${symbol})`),
},
options: {
animation: {
animateRotate: true,
animateScale: true,
},
},
});
}
}
9 changes: 9 additions & 0 deletions settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,15 @@
</a>
</span>
</p>
<p>
Chart.js:
<span>
https://www.chartjs.org/
<a href="https://www.chartjs.org/" target="_blank" title="Visit external url">
<i class="fa-solid fa-arrow-up-right-from-square"></i>
</a>
</span>
</p>
</div>
</section>

Expand Down
106 changes: 93 additions & 13 deletions stats.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,18 @@ function getPriceConverted($price, $currency, $database) {
$members[$memberId] = $row;
$memberCost[$memberId]['cost'] = 0;
$memberCost[$memberId]['name'] = $row['name'];
}
}

// Get categories
$categories = array();
$query = "SELECT * FROM categories";
$result = $db->query($query);
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$categoryId = $row['id'];
$categories[$categoryId] = $row;
$categoryCost[$categoryId]['cost'] = 0;
$categoryCost[$categoryId]['name'] = $row['name'];
}

// Get symbol of main currency to display on statistics
$query = "SELECT c.symbol
Expand All @@ -76,7 +87,8 @@ function getPriceConverted($price, $currency, $database) {
$mostExpensiveSubscription = 0;
$amountDueThisMonth = 0;
$totalCostPerMonth = 0;
$query = "SELECT name, price, frequency, cycle, currency_id, next_payment, payer_user_id FROM subscriptions";

$query = "SELECT name, price, frequency, cycle, currency_id, next_payment, payer_user_id, category_id FROM subscriptions";
$result = $db->query($query);
if ($result) {
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
Expand All @@ -91,10 +103,12 @@ function getPriceConverted($price, $currency, $database) {
$currency = $subscription['currency_id'];
$next_payment = $subscription['next_payment'];
$payerId = $subscription['payer_user_id'];
$categoryId = $subscription['category_id'];
$originalSubscriptionPrice = getPriceConverted($price, $currency, $db);
$price = getPricePerMonth($cycle, $frequency, $originalSubscriptionPrice);
$totalCostPerMonth += $price;
$memberCost[$payerId]['cost'] += $price;
$categoryCost[$categoryId]['cost'] += $price;
if ($price > $mostExpensiveSubscription) {
$mostExpensiveSubscription = $price;
}
Expand All @@ -103,7 +117,13 @@ function getPriceConverted($price, $currency, $database) {
if ((int)$memberCost[$payerId]['cost'] == $memberCost[$payerId]['cost']) {
$memberCost[$payerId]['cost'] = (int)$memberCost[$payerId]['cost'];
}

$categoryCost[$categoryId]['cost'] = number_format($categoryCost[$categoryId]['cost'], 2, ".", "");
if ((int)$categoryCost[$categoryId]['cost'] == $categoryCost[$categoryId]['cost']) {
$categoryCost[$categoryId]['cost'] = (int)$categoryCost[$categoryId]['cost'];
}

// Calculate ammount due this month
$nextPaymentDate = DateTime::createFromFormat('Y-m-d', trim($next_payment));
$tomorrow = new DateTime('tomorrow');
$endOfMonth = new DateTime('last day of this month');
Expand All @@ -122,6 +142,7 @@ function getPriceConverted($price, $currency, $database) {
}
$amountDueThisMonth += $originalSubscriptionPrice * $timesToPay;
}

}
$mostExpensiveSubscription = number_format($mostExpensiveSubscription, 2, ".", "");

Expand Down Expand Up @@ -151,6 +172,7 @@ function getPriceConverted($price, $currency, $database) {

?>
<section class="contain">
<h2>General Statistics</h2>
<div class="statistics">
<div class="statistic">
<span><?= $activeSubscriptions ?></span>
Expand Down Expand Up @@ -178,23 +200,81 @@ function getPriceConverted($price, $currency, $database) {
</div>
<?php
$numberOfElements = 6;
foreach($memberCost as $member) {
?>
<div class="statistic">
<span><?= $member['cost'] ?><?= $symbol ?></span>
<div class="title"><?= $member['name'] ?>'s monthly costs</div>
</div>
<?php
$numberOfElements++;
}
if (($numberOfElements + 1) % 3 == 0) {
?>
<div class="statistic empty"><div>
<div class="statistic empty"></div>
<?php
}
?>
?>
</div>
<h2>Split Views</h2>
<div class="graphs">
<?php

foreach ($categoryCost as $category) {
if ($category['cost'] != 0) {
$categoryDataPoints[] = [
"label" => $category['name'],
"y" => $category["cost"],
];
}
}

$showCategoryCostGraph = count($categoryCost) > 1;

foreach ($memberCost as $member) {
if ($member['cost'] != 0) {
$memberDataPoints[] = [
"label" => $member['name'],
"y" => $member["cost"],
];

}
}

$showMemberCostGraph = count($memberCost) > 1;

if ($showMemberCostGraph) {
?>
<section class="graph">
<header>
Household Split
<div class="sub-header">(Monthly cost)</div>
</header>
<canvas id="memberSplitChart"></canvas>
</section>
<?php
}

if ($showCategoryCostGraph) {
?>
<section class="graph">
<header>
Category Split
<div class="sub-header">(Monthly cost)</div>
</header>
<canvas id="categorySplitChart" style="height: 370px; width: 100%;"></canvas>
</section>
<?php
}

?>
</div>
</section>
<?php
if ($showCategoryCostGraph || $showMemberCostGraph || $showCostPerMonthGraph) {
?>
<script src="scripts/libs/chart.js"></script>
<script type="text/javascript">
window.onload = function() {
loadGraph("categorySplitChart", <?php echo json_encode($categoryDataPoints, JSON_NUMERIC_CHECK); ?>, "<?= $symbol ?>", <?= $showCategoryCostGraph ?>);
loadGraph("memberSplitChart", <?php echo json_encode($memberDataPoints, JSON_NUMERIC_CHECK); ?>, "<?= $symbol ?>", <?= $showMemberCostGraph ?>);
}
</script>
<?php
}
?>
<script src="scripts/stats.js"></script>
<?php
require_once 'includes/footer.php';
?>
3 changes: 2 additions & 1 deletion styles/dark-theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ header .logo .logo-image {
.logo-search,
.dropdown-content,
.sort-options,
.statistic {
.statistic,
.graph {
background-color: #222;
border: 1px solid #333;
box-shadow: 0 2px 5px rgba(120, 120, 120, 0.1);
Expand Down
8 changes: 4 additions & 4 deletions styles/login.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ body, html {
max-width: 100%;
padding: 20px;
background-color: #fff;
border-radius: 5px;
border-radius: 8px;
border: 1px solid #eee;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
Expand Down Expand Up @@ -80,7 +80,7 @@ select {
padding: 15px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
border-radius: 8px;
outline: none;
}

Expand All @@ -91,7 +91,7 @@ input[type="submit"] {
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
border-radius: 8px;
cursor: pointer;
}

Expand All @@ -107,7 +107,7 @@ input[type="checkbox"] {
margin: 0px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
border-radius: 8px;
display: grid;
place-content: center;
}
Expand Down
62 changes: 52 additions & 10 deletions styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ main > .contain {
background-color: #007bff;
padding: 12px 30px;
font-size: 1rem;
border-radius: 5px;
border-radius: 8px;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
text-decoration: none;
}
Expand All @@ -190,7 +190,7 @@ main > .contain {
background-color: #FFFFFF;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
padding: 12px 15px;
border-radius: 5px;
border-radius: 8px;
}

.subscription > span {
Expand Down Expand Up @@ -295,7 +295,7 @@ main > .contain {
border: 1px solid #eee;
padding: 20px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
border-radius: 5px;
border-radius: 8px;
}

.account-section header h2 {
Expand Down Expand Up @@ -497,6 +497,7 @@ main > .contain {
display: flex;
flex-direction: column;
gap: 16px;
line-break: anywhere;
}

.credits-list > p {
Expand Down Expand Up @@ -586,7 +587,7 @@ select {
font-size: 16px;
background-color: #FFFFFF;
border: 1px solid #ccc;
border-radius: 5px;
border-radius: 8px;
outline: none;
color: #202020;
box-sizing: border-box;
Expand Down Expand Up @@ -624,7 +625,7 @@ input[type="button"] {
background-color: #007bff;
color: #fff;
border: none;
border-radius: 5px;
border-radius: 8px;
cursor: pointer;
box-sizing: border-box;
border: 2px solid #007bff;
Expand Down Expand Up @@ -674,7 +675,7 @@ input[type="checkbox"] {
margin: 0px;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
border-radius: 8px;
display: grid;
place-content: center;
}
Expand All @@ -697,7 +698,7 @@ input[type="checkbox"] {
overflow-x: hidden;
padding: 10px;
border: 1px solid #EEEEEE;
border-radius: 5px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
background-color: white;
box-sizing: border-box;
Expand Down Expand Up @@ -758,7 +759,7 @@ input[type="checkbox"] {
background-color: #FFFFFF;
padding: 22px;
border: 1px solid #EEEEEE;
border-radius: 5px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
overflow: hidden;
Expand Down Expand Up @@ -852,7 +853,7 @@ input[type="checkbox"] {
font-size: 16px;
background-color: #FFFFFF;
border: 1px solid #EEEEEE;
border-radius: 5px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
box-sizing: border-box;
top: 48px;
Expand Down Expand Up @@ -1040,7 +1041,7 @@ input[type="checkbox"] {
.statistic {
background-color: #FFFFFF;
border: 1px solid #EEEEEE;
border-radius: 5px;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
padding: 20px 24px 30px;
display: flex;
Expand Down Expand Up @@ -1074,4 +1075,45 @@ input[type="checkbox"] {

.statistic > .title {
text-align: center;
}

.graphs {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 20px;
justify-content: space-between;
}

.graph {
background-color: #FFFFFF;
border: 1px solid #EEEEEE;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
flex-basis: 48%;
align-items: center;
justify-content: center;
display: flex;
flex-direction: column;
padding: 20px 10px;
box-sizing: border-box;
}

.graph > header {
font-size: 18px;
font-weight: 500;
margin-bottom: 15px;
text-align: center;
}

.graph > header > .sub-header {
font-size: 13px;
font-weight: normal;
}

@media (max-width: 768px) {
.graph {
flex-basis: 100%;
max-width: 100%;
}
}

0 comments on commit 190c052

Please sign in to comment.