diff --git a/assets/js/main.js b/assets/js/main.js
index 1059fd7..ef8df8d 100644
--- a/assets/js/main.js
+++ b/assets/js/main.js
@@ -1157,6 +1157,53 @@
window.alert(err.message || 'Could not reverse transaction');
});
});
+
+ var periodDataEl = document.getElementById('currencyLeaderboardPeriodData');
+ var periodBodyEl = document.getElementById('currencyLeaderboardPeriodBody');
+ var periodTabsEl = document.getElementById('currencyLeaderboardPeriodTabs');
+ if (periodDataEl && periodBodyEl && periodTabsEl) {
+ var periodData = {};
+ try {
+ periodData = JSON.parse(periodDataEl.textContent || '{}');
+ } catch (e) {
+ periodData = {};
+ }
+
+ function renderLeaderboardPeriod(periodKey) {
+ var rows = Array.isArray(periodData[periodKey]) ? periodData[periodKey] : [];
+ var sym = periodBodyEl.getAttribute('data-currency-symbol') || '';
+ var html = '';
+ rows.forEach(function (row, idx) {
+ var total = Number(row.period_total || 0);
+ var isSelf = Boolean(row.is_self);
+ html += '
';
+ html += '| ' + String(idx + 1) + ' | ';
+ html += '' + escapeHtml(row.name || '') + (isSelf ? ' You' : '') + ' | ';
+ html += '' + escapeHtml(total.toFixed(2)) + ' ' + escapeHtml(sym) + ' | ';
+ html += '
';
+ });
+ periodBodyEl.innerHTML = html;
+ }
+
+ periodTabsEl.querySelectorAll('[data-lb-period]').forEach(function (btn) {
+ btn.addEventListener('click', function () {
+ var next = btn.getAttribute('data-lb-period') || 'weekly';
+ periodTabsEl.querySelectorAll('[data-lb-period]').forEach(function (b) {
+ b.classList.remove('active');
+ b.setAttribute('aria-selected', 'false');
+ });
+ btn.classList.add('active');
+ btn.setAttribute('aria-selected', 'true');
+ renderLeaderboardPeriod(next);
+
+ var params = new URLSearchParams(window.location.search);
+ params.set('tab', 'currency');
+ params.set('lb_period', next);
+ var nextUrl = window.location.pathname + '?' + params.toString();
+ window.history.replaceState({}, '', nextUrl);
+ });
+ });
+ }
}
function bindPeopleTable() {
diff --git a/tabs/currency.php b/tabs/currency.php
index 014ed7b..ad8ede0 100644
--- a/tabs/currency.php
+++ b/tabs/currency.php
@@ -14,6 +14,7 @@ $bankingMode = bankingEnabled($familySettings);
$canRecordExpense = $activePerson !== null
&& ($activePerson['role'] ?? '') === ROLE_HEAD
&& isHohVerified();
+$activePersonId = (string) ($activePerson['id'] ?? '');
$people = migrateAllPeople($people);
if ($bankingMode) {
@@ -32,6 +33,12 @@ usort($leaderboard, static function ($a, $b) {
return strcmp((string) ($a['name'] ?? ''), (string) ($b['name'] ?? ''));
});
+$leaderboardPeriod = trim((string) ($_GET['lb_period'] ?? 'weekly'));
+$allowedLeaderboardPeriods = ['weekly', 'monthly', 'quarterly', 'yearly'];
+if (!in_array($leaderboardPeriod, $allowedLeaderboardPeriods, true)) {
+ $leaderboardPeriod = 'weekly';
+}
+
$nameById = [];
foreach ($people as $p) {
if (!empty($p['id'])) {
@@ -60,6 +67,90 @@ usort($bankRows, static function ($a, $b) {
return strcmp((string) ($b['created_at'] ?? ''), (string) ($a['created_at'] ?? ''));
});
$recentBank = array_slice($bankRows, 0, 80);
+$leaderboardRowsByPeriod = [];
+foreach ($allowedLeaderboardPeriods as $periodKey) {
+ $periodStart = new DateTimeImmutable('today');
+ if ($periodKey === 'monthly') {
+ $periodStart = $periodStart->modify('first day of this month');
+ } elseif ($periodKey === 'quarterly') {
+ $month = (int) $periodStart->format('n');
+ $quarterStartMonth = ((int) floor(($month - 1) / 3) * 3) + 1;
+ $periodStart = $periodStart->setDate((int) $periodStart->format('Y'), $quarterStartMonth, 1);
+ } elseif ($periodKey === 'yearly') {
+ $periodStart = $periodStart->setDate((int) $periodStart->format('Y'), 1, 1);
+ } else {
+ $periodStart = $periodStart->modify('monday this week');
+ }
+ $totalsByPerson = [];
+ foreach ($people as $p) {
+ $pid = (string) ($p['id'] ?? '');
+ if ($pid !== '') {
+ $totalsByPerson[$pid] = 0.0;
+ }
+ }
+ foreach ($bankRows as $row) {
+ if (!is_array($row)) {
+ continue;
+ }
+ $pid = (string) ($row['person_id'] ?? '');
+ if ($pid === '' || !array_key_exists($pid, $totalsByPerson)) {
+ continue;
+ }
+ $createdAt = trim((string) ($row['created_at'] ?? ''));
+ if ($createdAt === '') {
+ continue;
+ }
+ try {
+ $rowDate = new DateTimeImmutable($createdAt);
+ } catch (Exception $e) {
+ continue;
+ }
+ if ($rowDate < $periodStart) {
+ continue;
+ }
+ $amount = (float) ($row['amount'] ?? 0);
+ $type = (string) ($row['type'] ?? '');
+ if ($type === 'manual_credit' || $type === 'income_chore' || $type === 'reversal') {
+ $totalsByPerson[$pid] += $amount;
+ continue;
+ }
+ if ($type === 'manual_debit' || $type === 'charity_outflow') {
+ $totalsByPerson[$pid] -= $amount;
+ continue;
+ }
+ if ($type === 'transfer') {
+ $from = (string) ($row['from_account'] ?? '');
+ $to = (string) ($row['to_account'] ?? '');
+ if ($from === 'checking') {
+ $totalsByPerson[$pid] -= $amount;
+ } elseif ($to === 'checking') {
+ $totalsByPerson[$pid] += $amount;
+ }
+ }
+ }
+ $rows = [];
+ foreach ($people as $p) {
+ $pid = (string) ($p['id'] ?? '');
+ if ($pid === '') {
+ continue;
+ }
+ $rows[] = [
+ 'id' => $pid,
+ 'name' => (string) ($p['name'] ?? ''),
+ 'period_total' => bankingRoundMoney((float) ($totalsByPerson[$pid] ?? 0)),
+ 'is_self' => $activePerson && ($activePerson['id'] ?? '') === $pid,
+ ];
+ }
+ usort($rows, static function ($a, $b) {
+ $cmp = ((float) ($b['period_total'] ?? 0)) <=> ((float) ($a['period_total'] ?? 0));
+ if ($cmp !== 0) {
+ return $cmp;
+ }
+ return strcmp((string) ($a['name'] ?? ''), (string) ($b['name'] ?? ''));
+ });
+ $leaderboardRowsByPeriod[$periodKey] = $rows;
+}
+$leaderboardByPeriodRows = $leaderboardRowsByPeriod[$leaderboardPeriod] ?? [];
$donatedByPersonThisMonth = [];
foreach ($bankRows as $row) {
if (!is_array($row)) {
@@ -78,7 +169,6 @@ foreach ($bankRows as $row) {
}
$donatedByPersonThisMonth[$pid] = ($donatedByPersonThisMonth[$pid] ?? 0) + (float) ($row['amount'] ?? 0);
}
-$activePersonId = (string) ($activePerson['id'] ?? '');
$activeBankPerson = null;
foreach ($people as $p) {
if (($p['id'] ?? '') === $activePersonId) {
@@ -111,22 +201,33 @@ $activeCharityDonated = is_numeric($activeBankPerson['charity_donated_total'] ??
-
+
+
| # |
Name |
- Total = htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?> |
+ Net = htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?> |
-
- $p): ?>
+
+ $p): ?>